feat(系统管理): 组织模板管理页面和封装表单级联formCreate
This commit is contained in:
parent
8a711405eb
commit
a7538c1fad
|
@ -9,20 +9,45 @@ import {
|
||||||
GetFieldDetailUrl,
|
GetFieldDetailUrl,
|
||||||
GetOrganizeTemplateDetailUrl,
|
GetOrganizeTemplateDetailUrl,
|
||||||
GetOrganizeTemplateUrl,
|
GetOrganizeTemplateUrl,
|
||||||
|
GetProjectTemplateDetailUrl,
|
||||||
|
isEnableTemplateUrl,
|
||||||
SetOrganizeTemplateUrl,
|
SetOrganizeTemplateUrl,
|
||||||
UpdateFieldUrl,
|
UpdateFieldUrl,
|
||||||
|
UpdateOrganizeTemplateUrl,
|
||||||
UpdateProjectTemplateUrl,
|
UpdateProjectTemplateUrl,
|
||||||
} from '@/api/requrls/setting/template';
|
} from '@/api/requrls/setting/template';
|
||||||
|
|
||||||
import { CommonList, TableQueryParams } from '@/models/common';
|
import { CommonList, TableQueryParams } from '@/models/common';
|
||||||
import type { AddOrUpdateField, DefinedFieldItem, OrganizeTemplateItem } from '@/models/setting/template';
|
import type {
|
||||||
|
ActionTemplateManage,
|
||||||
|
AddOrUpdateField,
|
||||||
|
DefinedFieldItem,
|
||||||
|
OrganizeTemplateItem,
|
||||||
|
} from '@/models/setting/template';
|
||||||
|
|
||||||
/** *
|
/** *
|
||||||
* 模版
|
* 模版
|
||||||
*/
|
*/
|
||||||
// 获取模版列表(组织)
|
// 获取模版列表(组织)
|
||||||
export function getOrganizeTemplateList(organizationId: string, scene: string) {
|
export function getOrganizeTemplateList(params: TableQueryParams) {
|
||||||
return MSR.get<OrganizeTemplateItem[]>({ url: GetOrganizeTemplateUrl, params: `/${organizationId}/${scene}` });
|
return MSR.get({ url: `${GetOrganizeTemplateUrl}/${params.organizationId}/${params.scene}` });
|
||||||
|
}
|
||||||
|
// 获取模版详情
|
||||||
|
export function getOrganizeTemplateInfo(id: string) {
|
||||||
|
return MSR.get({ url: `${GetOrganizeTemplateDetailUrl}/${id}` });
|
||||||
|
}
|
||||||
|
// 创建模板列表(组织)
|
||||||
|
export function createOrganizeTemplateInfo(data: ActionTemplateManage) {
|
||||||
|
return MSR.post({ url: `${CreateOrganizeTemplateUrl}`, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑模板列表(组织)
|
||||||
|
export function updateOrganizeTemplateInfo(data: ActionTemplateManage) {
|
||||||
|
return MSR.post({ url: `${UpdateOrganizeTemplateUrl}`, data });
|
||||||
|
}
|
||||||
|
// 是否启用组织XX模板
|
||||||
|
export function isEnableTemplate(organizationId: string, scene: string) {
|
||||||
|
return MSR.get<boolean>({ url: `${isEnableTemplateUrl}/${organizationId}/${scene}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** *
|
/** *
|
||||||
|
|
|
@ -29,6 +29,8 @@ export const GetOrganizeTemplateDetailUrl = '/organization/template/get';
|
||||||
export const DeleteOrganizeTemplateUrl = '/organization/template/delete';
|
export const DeleteOrganizeTemplateUrl = '/organization/template/delete';
|
||||||
// 关闭组织模板,开启项目模版
|
// 关闭组织模板,开启项目模版
|
||||||
export const EnableOrOffTemplateUrl = '/organization/template/disable';
|
export const EnableOrOffTemplateUrl = '/organization/template/disable';
|
||||||
|
// 是否启用组织模板
|
||||||
|
export const isEnableTemplateUrl = '/organization/template/is-enable';
|
||||||
|
|
||||||
// 系统设置-组织-自定义字段
|
// 系统设置-组织-自定义字段
|
||||||
|
|
||||||
|
|
|
@ -209,6 +209,7 @@
|
||||||
.arco-select-view,
|
.arco-select-view,
|
||||||
.arco-select-view-single,
|
.arco-select-view-single,
|
||||||
.arco-select {
|
.arco-select {
|
||||||
|
width: 100%;
|
||||||
border: 1px solid var(--color-text-input-border);
|
border: 1px solid var(--color-text-input-border);
|
||||||
background-color: var(--color-text-fff);
|
background-color: var(--color-text-fff);
|
||||||
&:not(:disabled):hover {
|
&:not(:disabled):hover {
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
<MsTransfer
|
<MsTransfer
|
||||||
v-model="target"
|
v-model="target"
|
||||||
:data="treeList"
|
:data="treeList"
|
||||||
|
:title="[t('system.user.batchOptional'), t('system.user.batchChosen')]"
|
||||||
:tree-filed="{
|
:tree-filed="{
|
||||||
key: 'id',
|
key: 'id',
|
||||||
title: 'name',
|
title: 'name',
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
function jumpTo(name: RouteRecordName) {
|
function jumpTo(name: RouteRecordName) {
|
||||||
router.push({ name });
|
router.push({ name, query: route.query });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
import { FormRule } from '@form-create/arco-design';
|
||||||
|
|
||||||
|
// 表单字段使用
|
||||||
|
export const INPUT = {
|
||||||
|
type: 'input',
|
||||||
|
title: '',
|
||||||
|
field: 'fieldName',
|
||||||
|
value: '',
|
||||||
|
props: {
|
||||||
|
placeholder: '请输入',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export const SELECT = {
|
||||||
|
type: 'SearchSelect',
|
||||||
|
field: 'fieldName',
|
||||||
|
title: '',
|
||||||
|
value: '',
|
||||||
|
options: [],
|
||||||
|
props: {
|
||||||
|
multiple: false,
|
||||||
|
placeholder: '请选择',
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MULTIPLE_SELECT = {
|
||||||
|
type: 'SearchSelect',
|
||||||
|
field: 'fieldName',
|
||||||
|
title: '',
|
||||||
|
value: [],
|
||||||
|
options: [],
|
||||||
|
props: {
|
||||||
|
multiple: true,
|
||||||
|
placeholder: '请选择',
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RADIO = {
|
||||||
|
type: 'radio',
|
||||||
|
field: 'fieldName',
|
||||||
|
title: '',
|
||||||
|
value: '',
|
||||||
|
options: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CHECKBOX = {
|
||||||
|
type: 'checkbox',
|
||||||
|
field: 'fieldName',
|
||||||
|
title: '',
|
||||||
|
value: [],
|
||||||
|
options: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MEMBER = {
|
||||||
|
type: 'SearchSelect',
|
||||||
|
field: 'fieldName',
|
||||||
|
title: '',
|
||||||
|
value: '',
|
||||||
|
options: [],
|
||||||
|
props: {
|
||||||
|
multiple: false,
|
||||||
|
placeholder: '请选择',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MULTIPLE_MEMBER = {
|
||||||
|
type: 'SearchSelect',
|
||||||
|
field: 'fieldName',
|
||||||
|
title: '',
|
||||||
|
value: '',
|
||||||
|
options: [],
|
||||||
|
props: {
|
||||||
|
multiple: true,
|
||||||
|
placeholder: '请选择',
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DATE = {
|
||||||
|
type: 'DatePicker',
|
||||||
|
field: 'fieldName',
|
||||||
|
title: '',
|
||||||
|
value: '',
|
||||||
|
props: {
|
||||||
|
'placeholder': '请选择',
|
||||||
|
'format': 'YYYY/MM/DD',
|
||||||
|
'show-time': false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DATETIME = {
|
||||||
|
type: 'DatePicker',
|
||||||
|
field: 'fieldName',
|
||||||
|
title: '',
|
||||||
|
value: '',
|
||||||
|
props: {
|
||||||
|
'placeholder': '请选择',
|
||||||
|
'format': 'YYYY/MM/DD HH:mm:ss',
|
||||||
|
'show-time': true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FLOAT = {
|
||||||
|
type: 'InputNumber',
|
||||||
|
field: 'fieldName',
|
||||||
|
title: '',
|
||||||
|
value: 0,
|
||||||
|
props: {
|
||||||
|
placeholder: '请输入',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const INT = {
|
||||||
|
type: 'InputNumber',
|
||||||
|
field: 'fieldName',
|
||||||
|
title: '',
|
||||||
|
value: 0,
|
||||||
|
props: {
|
||||||
|
precision: 0,
|
||||||
|
placeholder: '请输入',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MULTIPLE_INPUT = {
|
||||||
|
type: 'a-input-tag',
|
||||||
|
field: 'fieldName',
|
||||||
|
title: '',
|
||||||
|
value: [],
|
||||||
|
props: {
|
||||||
|
placeholder: '请选择',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TEXTAREA = {
|
||||||
|
type: 'a-textarea',
|
||||||
|
field: 'fieldName',
|
||||||
|
title: '',
|
||||||
|
value: '',
|
||||||
|
props: {
|
||||||
|
'placeholder': '请输入',
|
||||||
|
'auto-size': {
|
||||||
|
minRows: 1,
|
||||||
|
maxRows: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FieldTypeFormRules: Record<string, FormRule> = {
|
||||||
|
INPUT,
|
||||||
|
SELECT,
|
||||||
|
MULTIPLE_SELECT,
|
||||||
|
RADIO,
|
||||||
|
CHECKBOX,
|
||||||
|
MEMBER,
|
||||||
|
MULTIPLE_MEMBER,
|
||||||
|
DATE,
|
||||||
|
DATETIME,
|
||||||
|
INT,
|
||||||
|
FLOAT,
|
||||||
|
MULTIPLE_INPUT,
|
||||||
|
TEXTAREA,
|
||||||
|
};
|
|
@ -0,0 +1,109 @@
|
||||||
|
<template>
|
||||||
|
<FormCreate v-model:api="formApi" :rule="formRules" :option="props.options || option"></FormCreate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
import { debounce } from 'lodash-es';
|
||||||
|
|
||||||
|
import PassWord from './formcreate-password.vue';
|
||||||
|
import SearchSelect from './searchSelect.vue';
|
||||||
|
|
||||||
|
import useFormCreateStore from '@/store/modules/form-create/form-create';
|
||||||
|
|
||||||
|
import { FormCreateKeyEnum } from '@/enums/formCreateEnum';
|
||||||
|
|
||||||
|
import type { FormItem } from './types';
|
||||||
|
import { FormRuleItem } from './types';
|
||||||
|
import formCreate, { FormRule } from '@form-create/arco-design';
|
||||||
|
|
||||||
|
const formCreateStore = useFormCreateStore();
|
||||||
|
|
||||||
|
formCreate.component('PassWord', PassWord);
|
||||||
|
formCreate.component('SearchSelect', SearchSelect);
|
||||||
|
|
||||||
|
const FormCreate = formCreate.$form();
|
||||||
|
const option = {
|
||||||
|
resetBtn: false, // 不展示默认配置的重置和提交
|
||||||
|
submitBtn: false,
|
||||||
|
on: false, // 取消绑定on事件
|
||||||
|
form: {
|
||||||
|
layout: 'vertical',
|
||||||
|
labelAlign: 'left',
|
||||||
|
},
|
||||||
|
// 暂时默认
|
||||||
|
row: {
|
||||||
|
gutter: 0,
|
||||||
|
},
|
||||||
|
wrap: {
|
||||||
|
'asterisk-position': 'end',
|
||||||
|
'validate-trigger': ['change'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// 处理配置项
|
||||||
|
const props = defineProps<{
|
||||||
|
options?: any; // 自定义配置
|
||||||
|
formRule: FormItem[]; // 表单的规则
|
||||||
|
formCreateKey: FormCreateKeyEnum[keyof FormCreateKeyEnum]; // 唯一表单Key
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const formApi = ref<any>({});
|
||||||
|
|
||||||
|
const formRules = ref<FormRule | undefined>([]);
|
||||||
|
|
||||||
|
// 计算被级联的项
|
||||||
|
const cascadeItem = computed(() => {
|
||||||
|
const currentFormCreateRules = formCreateStore.formCreateRuleMap.get(props.formCreateKey);
|
||||||
|
// 获取当前列表里边所有包含cascade的item
|
||||||
|
if (currentFormCreateRules) {
|
||||||
|
const cascade = currentFormCreateRules
|
||||||
|
.map((item: FormRuleItem) => item.link)
|
||||||
|
.filter((item) => item)
|
||||||
|
.flatMap((flatItem: any) => flatItem);
|
||||||
|
// 给所有的link上边关联的某个item 进行绑定监视
|
||||||
|
return currentFormCreateRules.filter((item: FormRuleItem) => {
|
||||||
|
return cascade.indexOf(item.field) > -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 计算远程检索的表单项
|
||||||
|
const getOptionsRequest = debounce((val: FormRuleItem) => {
|
||||||
|
// 获取当前变化的一项 监视到被级联的表单项
|
||||||
|
// 从所有的列表项里边获取所有的link到的那一项
|
||||||
|
const totalFormList = formCreateStore.formCreateRuleMap.get(props.formCreateKey);
|
||||||
|
if (totalFormList) {
|
||||||
|
const resultItem = totalFormList.find(
|
||||||
|
(item) => item.link && (item.link as string[]).indexOf(val.field as string) > -1
|
||||||
|
);
|
||||||
|
if (resultItem) formCreateStore.getOptions(val, props.formCreateKey, resultItem, formApi.value);
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
cascadeItem,
|
||||||
|
(val) => {
|
||||||
|
// 监视当前改变请求获取当前方法下边的options 和获取多有的字段值
|
||||||
|
if (val) {
|
||||||
|
val.forEach((item) => {
|
||||||
|
if (item.value) {
|
||||||
|
getOptionsRequest(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
formCreateStore.setInitFormCreate(props.formCreateKey, props.formRule);
|
||||||
|
formCreateStore.initFormCreateFormRules(props.formCreateKey);
|
||||||
|
formRules.value = formCreateStore.formCreateRuleMap.get(props.formCreateKey);
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
formApi, // 对外暴漏用于表单校验和清除校验状态 具体参考form-create文档API
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
@/store/modules/form-create/form-create
|
|
@ -6,10 +6,12 @@
|
||||||
import { ref, watch, watchEffect } from 'vue';
|
import { ref, watch, watchEffect } from 'vue';
|
||||||
|
|
||||||
import PassWord from './formcreate-password.vue';
|
import PassWord from './formcreate-password.vue';
|
||||||
|
import SearchSelect from './searchSelect.vue';
|
||||||
|
|
||||||
import formCreate, { FormRule } from '@form-create/arco-design';
|
import formCreate, { FormRule } from '@form-create/arco-design';
|
||||||
|
|
||||||
formCreate.component('PassWord', PassWord);
|
formCreate.component('PassWord', PassWord);
|
||||||
|
formCreate.component('SearchSelect', SearchSelect);
|
||||||
const FormCreate = formCreate.$form();
|
const FormCreate = formCreate.$form();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
@ -21,7 +23,7 @@
|
||||||
const emits = defineEmits<{
|
const emits = defineEmits<{
|
||||||
(e: 'update:api', val: any): void;
|
(e: 'update:api', val: any): void;
|
||||||
}>();
|
}>();
|
||||||
const formApi = ref({});
|
const formApi = ref<any>({});
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
formApi.value = props.api;
|
formApi.value = props.api;
|
||||||
|
@ -32,6 +34,18 @@
|
||||||
emits('update:api', val);
|
emits('update:api', val);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const formRules = ref<FormRule | undefined>([]);
|
||||||
|
watchEffect(() => {
|
||||||
|
formRules.value = props.rule;
|
||||||
|
});
|
||||||
|
watch(
|
||||||
|
() => props.rule,
|
||||||
|
(val) => {
|
||||||
|
formRules.value = val;
|
||||||
|
formApi.value?.refresh();
|
||||||
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
<template>
|
||||||
|
<a-select
|
||||||
|
v-model="selectValue"
|
||||||
|
:placeholder="t(props.placeholder || 'common.pleaseSelect')"
|
||||||
|
allow-search
|
||||||
|
:multiple="props.multiple"
|
||||||
|
@search="searchHandler"
|
||||||
|
>
|
||||||
|
<a-option v-for="opt of optionsList" :key="opt.value" :value="opt.value">{{ opt.label }}</a-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { debounce } from 'lodash-es';
|
||||||
|
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
optionMethod?: string; // 选项请求方法
|
||||||
|
inputSearch?: boolean; // 是否支持远程检索
|
||||||
|
modelValue: string[] | string | undefined; // 绑定值
|
||||||
|
keyword?: string; // 级联搜索关键词
|
||||||
|
formValue?: Record<string, any>; // 所有表单的值
|
||||||
|
options?: { label: string; value: string }[];
|
||||||
|
multiple?: boolean; // 是否多选
|
||||||
|
placeholder?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
inputSearch: false,
|
||||||
|
multiple: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:model-value']);
|
||||||
|
const selectValue = ref([]);
|
||||||
|
|
||||||
|
const optionsList = ref<{ label: string; value: string }[]>([]);
|
||||||
|
|
||||||
|
// 内部的关键词
|
||||||
|
const innerKeyword = ref<string | undefined>('');
|
||||||
|
async function getOptionsList() {
|
||||||
|
if (props.inputSearch && props.optionMethod) {
|
||||||
|
try {
|
||||||
|
setTimeout(() => {
|
||||||
|
// console.log('模拟请求');
|
||||||
|
optionsList.value = [
|
||||||
|
{
|
||||||
|
value: '111',
|
||||||
|
label: '测试测试测试111111',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, 1000);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const searchHandler = debounce(async (inputVal: string) => {
|
||||||
|
innerKeyword.value = inputVal;
|
||||||
|
getOptionsList();
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val) => {
|
||||||
|
selectValue.value = val as any;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => props.keyword,
|
||||||
|
(val) => {
|
||||||
|
if (val) {
|
||||||
|
innerKeyword.value = val;
|
||||||
|
getOptionsList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => selectValue.value,
|
||||||
|
(val) => {
|
||||||
|
emit('update:model-value', val);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (props.options) optionsList.value = props.options;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -0,0 +1,77 @@
|
||||||
|
import { FieldRule } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
import { FormRule } from '@form-create/arco-design';
|
||||||
|
|
||||||
|
export type FormItemType =
|
||||||
|
| 'INPUT'
|
||||||
|
| 'TEXTAREA'
|
||||||
|
| 'SELECT'
|
||||||
|
| 'MULTIPLE_SELECT'
|
||||||
|
| 'RADIO'
|
||||||
|
| 'CHECKBOX'
|
||||||
|
| 'MEMBER'
|
||||||
|
| 'MULTIPLE_MEMBER'
|
||||||
|
| 'DATE'
|
||||||
|
| 'DATETIME'
|
||||||
|
| 'INT'
|
||||||
|
| 'FLOAT'
|
||||||
|
| 'MULTIPLE_INPUT'
|
||||||
|
| 'INT'
|
||||||
|
| 'FLOAT'
|
||||||
|
| 'NUMBER';
|
||||||
|
|
||||||
|
// 表单选项
|
||||||
|
export interface FormItemComplexCommonConfig {
|
||||||
|
options: { label: string; value: string | number; disabled: boolean }[]; // 选择器、复选框组、单选框组选项列表
|
||||||
|
optionsRemoteMethodKey?: string; // 选择器、复选框组、单选框组选项列表远程搜索或初始化方法名
|
||||||
|
}
|
||||||
|
export interface FormItemDefaultOptions {
|
||||||
|
text: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FormRuleItem = FormRule & {
|
||||||
|
props: Record<string, any>;
|
||||||
|
};
|
||||||
|
// 表单配置项
|
||||||
|
export interface FormItem {
|
||||||
|
type: FormItemType;
|
||||||
|
name: string; // 表单项名称,作为唯一标志 --field
|
||||||
|
label: string; // 表单项文本 --- title
|
||||||
|
// 选择器的值绑定为Record<string, any>,避免携带远程搜索时默认选中的选项不在 options 列表中,所以还需要设置 fallbackOptions
|
||||||
|
value: string | number | boolean | string[] | number[] | Record<string, any> | Record<string, any>[];
|
||||||
|
subDesc?: string; // 表单项描述,在表单项下方显示
|
||||||
|
inputSearch?: boolean; // 是否支持远程搜索
|
||||||
|
tooltip?: ''; // 表单后边的提示info
|
||||||
|
instructionsIcon?: ''; // 是否有图片在表单后边展示
|
||||||
|
optionMethod?: string; // 请求检索的方法 两个参数 表单项的所有值
|
||||||
|
options?: FormItemDefaultOptions[];
|
||||||
|
required: boolean;
|
||||||
|
validate?: FieldRule[];
|
||||||
|
// 表单联动配置
|
||||||
|
couplingConfig?: {
|
||||||
|
// 联动类型,visible:显示隐藏,disabled:禁用启用,filterOptions:过滤选项,disabledOptions:禁用选项,initOptions:初始化选项。都由联动的表单项触发
|
||||||
|
type: 'visible' | 'disabled' | 'filterOptions' | 'disabledOptions' | 'initOptions'; // 目前初始化选项
|
||||||
|
cascade: string; // 联动表单项名称
|
||||||
|
matchRule: 'same' | 'includes' | 'excludes' | RegExp; // 联动匹配规则,same:值相同,includes:值包含,excludes:值不包含, RegExp:自定义匹配正则表达式 // 场景 目前只考虑等于情况
|
||||||
|
}[];
|
||||||
|
// 表单布局
|
||||||
|
wrap?: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FomItemSelect extends FormItemComplexCommonConfig {
|
||||||
|
selectMultiple?: boolean; // 选择器是否多选
|
||||||
|
selectMultipleLimit?: [number, number]; // 选择器多选时最少和最多可选项数,如:[1, 3],表示最少选1项,最多选3项;[0, 3]表示最多选3项,可不选;[1, 0]表示最少选1项,不限制最大可选数
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FomItemCheckbox extends FormItemComplexCommonConfig {
|
||||||
|
checkedAll?: boolean; // 复选框组是否支持全选
|
||||||
|
checkedAllLabel?: string; // 复选框组全选选项文本
|
||||||
|
checkedMax?: number; // 复选框组最多可选项数
|
||||||
|
direction?: 'horizontal' | 'vertical'; // 单选框组选项排列方向,默认为 'horizontal'
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormItemRadio extends FormItemComplexCommonConfig {
|
||||||
|
type?: 'radio' | 'button'; // 单选框组选项排列方式,默认为 'radio'
|
||||||
|
direction?: 'horizontal' | 'vertical'; // 单选框组选项排列方向,默认为 'horizontal'
|
||||||
|
}
|
|
@ -18,8 +18,8 @@
|
||||||
class="mr-[2px] text-xl text-[rgb(var(--danger-6))]"
|
class="mr-[2px] text-xl text-[rgb(var(--danger-6))]"
|
||||||
/>
|
/>
|
||||||
</slot>
|
</slot>
|
||||||
<span :class="titleClass">
|
<span :class="[titleClass]">
|
||||||
{{ characterLimit(props.title) || '' }}
|
{{ props.title || '' }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 描述展示 -->
|
<!-- 描述展示 -->
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
{{ props.cancelText || t('common.cancel') }}
|
{{ props.cancelText || t('common.cancel') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button type="primary" size="mini" :loading="props.loading" @click="handleConfirm">
|
<a-button type="primary" size="mini" :loading="props.loading" @click="handleConfirm">
|
||||||
{{ props.isDelete ? t('common.remove') : props.okText || t('common.confirm') }}
|
{{ t(props.okText) || t('common.confirm') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -79,7 +79,6 @@
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { characterLimit } from '@/utils';
|
|
||||||
|
|
||||||
import type { FieldRule, FormInstance } from '@arco-design/web-vue';
|
import type { FieldRule, FormInstance } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
@ -113,6 +112,7 @@
|
||||||
{
|
{
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
isDelete: true, // 默认移除pop
|
isDelete: true, // 默认移除pop
|
||||||
|
okText: 'common.remove',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const emits = defineEmits<{
|
const emits = defineEmits<{
|
||||||
|
@ -167,7 +167,7 @@
|
||||||
// 获取当前标题的样式
|
// 获取当前标题的样式
|
||||||
const titleClass = computed(() => {
|
const titleClass = computed(() => {
|
||||||
return props.isDelete
|
return props.isDelete
|
||||||
? 'ml-2 font-[14px] text-[var(--color-text-1)]'
|
? 'ml-2 font-medium text-[var(--color-text-1)] text-[14px]'
|
||||||
: 'mb-[8px] font-medium text-[var(--color-text-1)] text-[14px]';
|
: 'mb-[8px] font-medium text-[var(--color-text-1)] text-[14px]';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,13 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div style="height: 100vh" class="flex">
|
<div style="height: 140px" class="rich-wrapper flex w-full">
|
||||||
<RichTextEditor v-if="editor" :editor="editor" :locale="locale" />
|
<RichTextEditor v-if="editor" :editor="editor" :locale="locale" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.rich-wrapper {
|
||||||
|
border: 1px solid var(--color-text-n8);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -21,7 +21,7 @@ export interface Pagination {
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const tableStore = useTableStore();
|
const tableStore = useTableStore();
|
||||||
export default function useTableProps<T>(
|
export default function useTableProps<T>(
|
||||||
loadListFunc: (v?: TableQueryParams | any) => Promise<CommonList<MsTableDataItem<T>> | MsTableDataItem<T>>,
|
loadListFunc?: (v?: TableQueryParams | any) => Promise<CommonList<MsTableDataItem<T>> | MsTableDataItem<T>>,
|
||||||
props?: Partial<MsTableProps<T>>,
|
props?: Partial<MsTableProps<T>>,
|
||||||
// 数据处理的回调函数
|
// 数据处理的回调函数
|
||||||
dataTransform?: (item: TableData) => (TableData & T) | any,
|
dataTransform?: (item: TableData) => (TableData & T) | any,
|
||||||
|
@ -161,40 +161,42 @@ export default function useTableProps<T>(
|
||||||
const { current, pageSize } = propsRes.value.msPagination as Pagination;
|
const { current, pageSize } = propsRes.value.msPagination as Pagination;
|
||||||
const { rowKey, selectorStatus, excludeKeys } = propsRes.value;
|
const { rowKey, selectorStatus, excludeKeys } = propsRes.value;
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
if (loadListFunc) {
|
||||||
const data = await loadListFunc({
|
setLoading(true);
|
||||||
current,
|
const data = await loadListFunc({
|
||||||
pageSize,
|
current,
|
||||||
sort: sortItem.value,
|
pageSize,
|
||||||
filter: filterItem.value,
|
sort: sortItem.value,
|
||||||
keyword: keyword.value,
|
filter: filterItem.value,
|
||||||
...loadListParams.value,
|
keyword: keyword.value,
|
||||||
});
|
...loadListParams.value,
|
||||||
const tmpArr = data.list;
|
});
|
||||||
propsRes.value.data = tmpArr.map((item: MsTableDataItem<T>) => {
|
const tmpArr = data.list;
|
||||||
if (item.updateTime) {
|
propsRes.value.data = tmpArr.map((item: MsTableDataItem<T>) => {
|
||||||
item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss');
|
if (item.updateTime) {
|
||||||
}
|
item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss');
|
||||||
if (item.createTime) {
|
|
||||||
item.createTime = dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss');
|
|
||||||
}
|
|
||||||
if (dataTransform) {
|
|
||||||
item = dataTransform(item);
|
|
||||||
}
|
|
||||||
if (selectorStatus === SelectAllEnum.ALL) {
|
|
||||||
if (!excludeKeys.has(item[rowKey])) {
|
|
||||||
setTableSelected(item[rowKey]);
|
|
||||||
}
|
}
|
||||||
|
if (item.createTime) {
|
||||||
|
item.createTime = dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
}
|
||||||
|
if (dataTransform) {
|
||||||
|
item = dataTransform(item);
|
||||||
|
}
|
||||||
|
if (selectorStatus === SelectAllEnum.ALL) {
|
||||||
|
if (!excludeKeys.has(item[rowKey])) {
|
||||||
|
setTableSelected(item[rowKey]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
if (data.total === 0) {
|
||||||
|
setTableErrorStatus('empty');
|
||||||
|
} else {
|
||||||
|
setTableErrorStatus(false);
|
||||||
}
|
}
|
||||||
return item;
|
setPagination({ current: data.current, total: data.total });
|
||||||
});
|
return data;
|
||||||
if (data.total === 0) {
|
|
||||||
setTableErrorStatus('empty');
|
|
||||||
} else {
|
|
||||||
setTableErrorStatus(false);
|
|
||||||
}
|
}
|
||||||
setPagination({ current: data.current, total: data.total });
|
|
||||||
return data;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setTableErrorStatus('error');
|
setTableErrorStatus('error');
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -208,26 +210,28 @@ export default function useTableProps<T>(
|
||||||
} else {
|
} else {
|
||||||
// 没分页的情况下,直接调用loadListFunc
|
// 没分页的情况下,直接调用loadListFunc
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
if (loadListFunc) {
|
||||||
const data = await loadListFunc({ keyword: keyword.value, ...loadListParams.value });
|
setLoading(true);
|
||||||
if (data.length === 0) {
|
const data = await loadListFunc({ keyword: keyword.value, ...loadListParams.value });
|
||||||
setTableErrorStatus('empty');
|
if (data.length === 0) {
|
||||||
return;
|
setTableErrorStatus('empty');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTableErrorStatus(false);
|
||||||
|
propsRes.value.data = data.map((item: MsTableDataItem<T>) => {
|
||||||
|
if (item.updateTime) {
|
||||||
|
item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
}
|
||||||
|
if (item.createTime) {
|
||||||
|
item.createTime = dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
}
|
||||||
|
if (dataTransform) {
|
||||||
|
item = dataTransform(item);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
setTableErrorStatus(false);
|
|
||||||
propsRes.value.data = data.map((item: MsTableDataItem<T>) => {
|
|
||||||
if (item.updateTime) {
|
|
||||||
item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss');
|
|
||||||
}
|
|
||||||
if (item.createTime) {
|
|
||||||
item.createTime = dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss');
|
|
||||||
}
|
|
||||||
if (dataTransform) {
|
|
||||||
item = dataTransform(item);
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
return data;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
export enum FormCreateKeyEnum {
|
||||||
|
ORGANIZE_TEMPLATE = 'OrganizeTemplate',
|
||||||
|
ORGANIZE_TEMPLATE_PREVIEW_TEMPLATE = 'OrganizeTemplatePreview',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {};
|
|
@ -61,6 +61,8 @@ export enum SettingRouteEnum {
|
||||||
SETTING_ORGANIZATION_PROJECT = 'settingOrganizationProject',
|
SETTING_ORGANIZATION_PROJECT = 'settingOrganizationProject',
|
||||||
SETTING_ORGANIZATION_TEMPLATE = 'settingOrganizationTemplate',
|
SETTING_ORGANIZATION_TEMPLATE = 'settingOrganizationTemplate',
|
||||||
SETTING_ORGANIZATION_TEMPLATE_FILED_SETTING = 'settingOrganizationTemplateFiledSetting',
|
SETTING_ORGANIZATION_TEMPLATE_FILED_SETTING = 'settingOrganizationTemplateFiledSetting',
|
||||||
|
SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT = 'settingOrganizationTemplateManagement',
|
||||||
|
SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL = 'settingOrganizationTemplateManagementDetail',
|
||||||
SETTING_ORGANIZATION_SERVICE = 'settingOrganizationService',
|
SETTING_ORGANIZATION_SERVICE = 'settingOrganizationService',
|
||||||
SETTING_ORGANIZATION_LOG = 'settingOrganizationLog',
|
SETTING_ORGANIZATION_LOG = 'settingOrganizationLog',
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,10 @@ export enum TableKeyEnum {
|
||||||
PROJECT_MEMBER = 'projectMember',
|
PROJECT_MEMBER = 'projectMember',
|
||||||
PROJECT_USER_GROUP = 'projectUserGroup',
|
PROJECT_USER_GROUP = 'projectUserGroup',
|
||||||
ORGANIZATION_MEMBER = 'organizationMember',
|
ORGANIZATION_MEMBER = 'organizationMember',
|
||||||
ORGANIZATION_TEMPLATE = 'organizationTemplate',
|
ORGANIZATION_TEMPLATE_FIELD_SETTING = 'organizationTemplateFieldSetting',
|
||||||
|
ORGANIZATION_TEMPLATE_MANAGEMENT = 'organizationTemplateManagement',
|
||||||
|
ORGANIZATION_TEMPLATE_MANAGEMENT_FIELD = 'organizationTemplateManagementField',
|
||||||
|
ORGANIZATION_TEMPLATE_MANAGEMENT_STEP = 'organizationTemplateManagementStep',
|
||||||
ORGANIZATION_PROJECT = 'organizationProject',
|
ORGANIZATION_PROJECT = 'organizationProject',
|
||||||
ORGANIZATION_PROJECT_USER_DRAWER = 'organizationProjectUserDrawer',
|
ORGANIZATION_PROJECT_USER_DRAWER = 'organizationProjectUserDrawer',
|
||||||
FILE_MANAGEMENT_FILE = 'fileManagementFile',
|
FILE_MANAGEMENT_FILE = 'fileManagementFile',
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// 模版展示字段icon
|
||||||
export enum TemplateIconEnum {
|
export enum TemplateIconEnum {
|
||||||
INPUT = 'icon-icon_input', // 输入框
|
INPUT = 'icon-icon_input', // 输入框
|
||||||
TEXTAREA = 'icon-icon_style_one', // 文本
|
TEXTAREA = 'icon-icon_style_one', // 文本
|
||||||
|
@ -14,5 +15,16 @@ export enum TemplateIconEnum {
|
||||||
FLOAT = 'icon-icon_pound', // 数字-浮点型
|
FLOAT = 'icon-icon_pound', // 数字-浮点型
|
||||||
MULTIPLE_INPUT = 'icon-icon_tag', // 多值输入框、
|
MULTIPLE_INPUT = 'icon-icon_tag', // 多值输入框、
|
||||||
NUMBER = 'icon-icon_pound', // 数字
|
NUMBER = 'icon-icon_pound', // 数字
|
||||||
|
SYSTEM = 'icon-icon_pound',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 模版列表图标卡片icon
|
||||||
|
export enum TemplateCardEnum {
|
||||||
|
FUNCTIONAL = 'caseTemplate', // 用例模版
|
||||||
|
API = 'api_ui_Template', // ui模板
|
||||||
|
UI = 'api_ui_Template', // API模板
|
||||||
|
TEST_PLAN = 'testPlanTemplate', // 测试计划模板
|
||||||
|
BUG = 'defectTemplate', // 缺陷模板
|
||||||
|
}
|
||||||
|
|
||||||
export default {};
|
export default {};
|
||||||
|
|
|
@ -60,4 +60,5 @@ export default {
|
||||||
'common.allSelect': 'Select All',
|
'common.allSelect': 'Select All',
|
||||||
'common.setting': 'Setting',
|
'common.setting': 'Setting',
|
||||||
'common.resetDefault': 'Restore default',
|
'common.resetDefault': 'Restore default',
|
||||||
|
'common.pleaseSelect': 'Please Select',
|
||||||
};
|
};
|
||||||
|
|
|
@ -54,6 +54,9 @@ export default {
|
||||||
'menu.settings.organization.project': 'Project',
|
'menu.settings.organization.project': 'Project',
|
||||||
'menu.settings.organization.template': 'Template',
|
'menu.settings.organization.template': 'Template',
|
||||||
'menu.settings.organization.templateFieldSetting': 'fieldSetting',
|
'menu.settings.organization.templateFieldSetting': 'fieldSetting',
|
||||||
|
'menu.settings.organization.templateManagementList': 'Template list',
|
||||||
|
'menu.settings.organization.templateManagementEdit': 'Update Template',
|
||||||
|
'menu.settings.organization.templateManagementDetail': 'Create Template',
|
||||||
'menu.settings.organization.serviceIntegration': 'Service Integration',
|
'menu.settings.organization.serviceIntegration': 'Service Integration',
|
||||||
'menu.settings.organization.log': 'Log',
|
'menu.settings.organization.log': 'Log',
|
||||||
'navbar.action.locale': 'Switch to English',
|
'navbar.action.locale': 'Switch to English',
|
||||||
|
|
|
@ -64,4 +64,5 @@ export default {
|
||||||
'common.resetDefault': '恢复默认',
|
'common.resetDefault': '恢复默认',
|
||||||
'common.tagPlaceholder': '添加标签回车结束',
|
'common.tagPlaceholder': '添加标签回车结束',
|
||||||
'common.batchModify': '批量修改',
|
'common.batchModify': '批量修改',
|
||||||
|
'common.pleaseSelect': '请选择',
|
||||||
};
|
};
|
||||||
|
|
|
@ -54,6 +54,9 @@ export default {
|
||||||
'menu.settings.organization.serviceIntegration': '服务集成',
|
'menu.settings.organization.serviceIntegration': '服务集成',
|
||||||
'menu.settings.organization.template': '模版',
|
'menu.settings.organization.template': '模版',
|
||||||
'menu.settings.organization.templateFieldSetting': '字段设置',
|
'menu.settings.organization.templateFieldSetting': '字段设置',
|
||||||
|
'menu.settings.organization.templateManagementList': '模版列表',
|
||||||
|
'menu.settings.organization.templateManagementDetail': '创建模版',
|
||||||
|
'menu.settings.organization.templateManagementEdit': '更新模板',
|
||||||
'menu.settings.organization.log': '日志',
|
'menu.settings.organization.log': '日志',
|
||||||
'navbar.action.locale': '切换为中文',
|
'navbar.action.locale': '切换为中文',
|
||||||
...sys,
|
...sys,
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import { LocationQueryValue } from 'vue-router';
|
import { LocationQueryValue } from 'vue-router';
|
||||||
|
|
||||||
// 模版管理
|
import type { FormItem, FormItemType, FormRuleItem } from '@/components/pure/ms-form-create/types';
|
||||||
|
|
||||||
|
import { FormRule } from '@form-create/arco-design';
|
||||||
|
|
||||||
|
// 模版管理(组织)
|
||||||
export interface OrganizeTemplateItem {
|
export interface OrganizeTemplateItem {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -18,21 +22,33 @@ export interface OrganizeTemplateItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SeneType = 'FUNCTIONAL' | 'BUG' | 'API' | 'UI' | 'TEST_PLAN' | LocationQueryValue[] | LocationQueryValue;
|
export type SeneType = 'FUNCTIONAL' | 'BUG' | 'API' | 'UI' | 'TEST_PLAN' | LocationQueryValue[] | LocationQueryValue;
|
||||||
|
|
||||||
|
export interface FieldOptions {
|
||||||
|
fieldId?: string;
|
||||||
|
value: string | string[] | number | number[];
|
||||||
|
text: string;
|
||||||
|
internal?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
// 自定义字段
|
// 自定义字段
|
||||||
export interface DefinedFieldItem {
|
export interface DefinedFieldItem {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
scene: SeneType; // 使用场景
|
scene: SeneType; // 使用场景
|
||||||
type: string;
|
type: FormItemType; // 表单类型
|
||||||
remark: string;
|
remark: string;
|
||||||
internal: boolean; // 是否是内置字段
|
internal: boolean; // 是否是内置字段
|
||||||
scopeType: string; // 组织或项目级别字段(PROJECT, ORGANIZATION)
|
scopeType: string; // 组织或项目级别字段(PROJECT, ORGANIZATION)
|
||||||
createTime: number;
|
createTime: number;
|
||||||
updateTime: number;
|
updateTime: number;
|
||||||
createUser: string;
|
createUser: string;
|
||||||
refId: string; // 项目字段所关联的组织字段ID
|
refId: string | null; // 项目字段所关联的组织字段ID
|
||||||
enableOptionKey: boolean; // 是否需要手动输入选项key
|
enableOptionKey: boolean | null; // 是否需要手动输入选项key
|
||||||
scopeId: string; // 组织或项目ID
|
scopeId: string; // 组织或项目ID
|
||||||
|
options: FieldOptions[] | null;
|
||||||
|
required?: boolean | undefined;
|
||||||
|
fApi?: any; // 表单值
|
||||||
|
formRules?: FormRuleItem[] | FormItem[] | FormRule[]; // 表单列表
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建自定义字段
|
// 创建自定义字段
|
||||||
|
@ -42,12 +58,55 @@ export interface FieldOption {
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增 || 编辑参数
|
||||||
export interface AddOrUpdateField {
|
export interface AddOrUpdateField {
|
||||||
id?: string;
|
id?: string;
|
||||||
name: string;
|
name: string;
|
||||||
scene: SeneType; // 使用场景
|
scene: SeneType; // 使用场景
|
||||||
type: string;
|
type: FormItemType;
|
||||||
remark: string; // 备注
|
remark: string; // 备注
|
||||||
scopeId: string; // 组织或项目ID
|
scopeId: string; // 组织或项目ID
|
||||||
options?: FieldOption[];
|
options?: FieldOption[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface fieldIconAndNameModal {
|
||||||
|
key: string;
|
||||||
|
iconName: string; // 图标名称
|
||||||
|
label: string; // 对应标签
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模板管理列表(组织)
|
||||||
|
export interface OrdTemplateManagement {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
remark: string; // 描述
|
||||||
|
internal: boolean; // 是否是系统模板
|
||||||
|
updateTime: number;
|
||||||
|
createTime: number;
|
||||||
|
createUser: string; // 创建人
|
||||||
|
scopeType: string; // 组织或项目级别字段
|
||||||
|
scopeId: string; // 组织或项目ID
|
||||||
|
enableThirdPart: boolean; // 是否开启api字段名配置
|
||||||
|
enableDefault: boolean; // 是否是默认模板
|
||||||
|
refId: string; // 项目模板所关联的组织模板ID
|
||||||
|
scene: string; // 使用场景
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建模板& 更新模板管理项
|
||||||
|
|
||||||
|
export interface CustomField {
|
||||||
|
fieldId: string;
|
||||||
|
required: boolean; // 是否必填
|
||||||
|
apiFieldId: string; // api字段名
|
||||||
|
defaultValue: string; // 默认值
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ActionTemplateManage {
|
||||||
|
id?: string;
|
||||||
|
name: string;
|
||||||
|
remark: string;
|
||||||
|
scopeId: string;
|
||||||
|
enableThirdPart?: boolean; // 是否开启api字段名配置
|
||||||
|
scene: SeneType;
|
||||||
|
customFields: CustomField[];
|
||||||
|
}
|
||||||
|
|
|
@ -188,7 +188,7 @@ const Setting: AppRouteRecordRaw = {
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 模版字段设置
|
// 模板列表-模版字段设置
|
||||||
{
|
{
|
||||||
path: 'templateFiledSetting',
|
path: 'templateFiledSetting',
|
||||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_FILED_SETTING,
|
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_FILED_SETTING,
|
||||||
|
@ -209,6 +209,52 @@ const Setting: AppRouteRecordRaw = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// 模版管理-模版列表
|
||||||
|
{
|
||||||
|
path: 'templateManagement',
|
||||||
|
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT,
|
||||||
|
component: () => import('@/views/setting/organization/template/components/templateManagement.vue'),
|
||||||
|
meta: {
|
||||||
|
locale: 'menu.settings.organization.templateManagementList',
|
||||||
|
roles: ['*'],
|
||||||
|
breadcrumbs: [
|
||||||
|
{
|
||||||
|
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE,
|
||||||
|
locale: 'menu.settings.organization.template',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT,
|
||||||
|
locale: 'menu.settings.organization.templateManagementList',
|
||||||
|
editLocale: 'menu.settings.organization.templateManagementList',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 模板列表-模板管理-创建&编辑模版
|
||||||
|
{
|
||||||
|
path: 'templateManagementDetail',
|
||||||
|
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL,
|
||||||
|
component: () => import('@/views/setting/organization/template/components/templateDetail.vue'),
|
||||||
|
meta: {
|
||||||
|
locale: 'menu.settings.organization.templateManagementDetail',
|
||||||
|
roles: ['*'],
|
||||||
|
breadcrumbs: [
|
||||||
|
{
|
||||||
|
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE,
|
||||||
|
locale: 'menu.settings.organization.template',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT,
|
||||||
|
locale: 'menu.settings.organization.templateManagementList',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT,
|
||||||
|
locale: 'menu.settings.organization.templateManagementDetail',
|
||||||
|
editLocale: 'menu.settings.organization.templateManagementEdit',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'log',
|
path: 'log',
|
||||||
name: SettingRouteEnum.SETTING_ORGANIZATION_LOG,
|
name: SettingRouteEnum.SETTING_ORGANIZATION_LOG,
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
import { FieldTypeFormRules } from '@/components/pure/ms-form-create/form-create';
|
||||||
|
import type { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types';
|
||||||
|
|
||||||
|
import { FormCreateKeyEnum } from '@/enums/formCreateEnum';
|
||||||
|
|
||||||
|
const useFormCreateStore = defineStore('form-create', {
|
||||||
|
persist: false,
|
||||||
|
state: (): {
|
||||||
|
formRuleMap: Map<FormCreateKeyEnum[keyof FormCreateKeyEnum], FormItem[]>;
|
||||||
|
formCreateRuleMap: Map<FormCreateKeyEnum[keyof FormCreateKeyEnum], FormRuleItem[]>;
|
||||||
|
} => ({
|
||||||
|
formRuleMap: new Map(),
|
||||||
|
formCreateRuleMap: new Map(),
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
// 存储外边传递初始化数据格式存储form-item
|
||||||
|
setInitFormCreate(key: FormCreateKeyEnum[keyof FormCreateKeyEnum], formRule: FormItem[]) {
|
||||||
|
this.formRuleMap = new Map();
|
||||||
|
this.formRuleMap.set(key, formRule);
|
||||||
|
},
|
||||||
|
// 根据不同的类型初始化数据
|
||||||
|
initFormCreateFormRules(key: FormCreateKeyEnum[keyof FormCreateKeyEnum]) {
|
||||||
|
const currentFormRule = this.formRuleMap.get(key);
|
||||||
|
// 处理数据结构
|
||||||
|
const result = currentFormRule?.map((item: FormItem) => {
|
||||||
|
// 当前类型
|
||||||
|
let fieldType;
|
||||||
|
const currentTypeForm = Object.keys(FieldTypeFormRules).find(
|
||||||
|
(formItemType: any) => item.type.toUpperCase() === formItemType
|
||||||
|
);
|
||||||
|
if (currentTypeForm) {
|
||||||
|
fieldType = FieldTypeFormRules[currentTypeForm].type;
|
||||||
|
const options = item?.options;
|
||||||
|
const currentOptions = options?.map((optionsItem) => {
|
||||||
|
return {
|
||||||
|
label: optionsItem.text,
|
||||||
|
value: optionsItem.value,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
type: fieldType, // 表单类型
|
||||||
|
field: item.name, // 字段
|
||||||
|
title: item.label, // label 表单标签
|
||||||
|
value: FieldTypeFormRules[currentTypeForm].value, // 目前的值
|
||||||
|
effect: {
|
||||||
|
required: item.required, // 是否必填
|
||||||
|
},
|
||||||
|
// 级联关联到某一个form上 可能存在多个级联
|
||||||
|
options: !item.optionMethod ? currentOptions : [],
|
||||||
|
link: item.couplingConfig?.map((cascadeItem: any) => cascadeItem.cascade),
|
||||||
|
rule: item.validate || [],
|
||||||
|
// 梳理表单所需要属性
|
||||||
|
props: {
|
||||||
|
...FieldTypeFormRules[currentTypeForm].props,
|
||||||
|
'tooltip': item.tooltip,
|
||||||
|
// 表单后边展示图片
|
||||||
|
'instructionsIcon': item.instructionsIcon,
|
||||||
|
// 下拉选项请求 必须是开启远程搜索才有该方法
|
||||||
|
'subDesc': item.subDesc,
|
||||||
|
// 级联匹配规则
|
||||||
|
'couplingConfig': { ...item.couplingConfig },
|
||||||
|
'optionMethod': item.inputSearch && item.optionMethod ? item.optionMethod : '',
|
||||||
|
'inputSearch': item.inputSearch,
|
||||||
|
'allow-search': item.inputSearch,
|
||||||
|
'keyword': '',
|
||||||
|
'modelValue': item.value,
|
||||||
|
'options': currentOptions,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
if (result && result.length) {
|
||||||
|
this.setInitdRules(key, result as FormRuleItem[]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 初始化好了的格式给formCreate
|
||||||
|
setInitdRules(key: FormCreateKeyEnum[keyof FormCreateKeyEnum], result: FormRuleItem[]) {
|
||||||
|
this.formCreateRuleMap.set(key, result);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** **
|
||||||
|
* @description 处理监视联动获取请求
|
||||||
|
* @param key: 对应Map的Key
|
||||||
|
* @param item: 当前对应关联项-请求改变options
|
||||||
|
* @param formValueApi: 当前表单值实例可以获取表单的当前已经设置的值
|
||||||
|
*/
|
||||||
|
async getOptions(
|
||||||
|
val: FormRuleItem,
|
||||||
|
key: FormCreateKeyEnum[keyof FormCreateKeyEnum],
|
||||||
|
cascadeItem: FormRuleItem,
|
||||||
|
formValueApi: any
|
||||||
|
) {
|
||||||
|
const formValue = formValueApi.formData();
|
||||||
|
// 设置自定义属性给到searchSelect
|
||||||
|
const formCreateRuleArr = this.formCreateRuleMap.get(key);
|
||||||
|
const formCreateItem = formCreateRuleArr?.find((items: FormRuleItem) => cascadeItem.field === items.field);
|
||||||
|
if (formCreateItem) {
|
||||||
|
formCreateItem.props.keyword = val.value;
|
||||||
|
formCreateItem.props.formValue = formValue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
getters: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default useFormCreateStore;
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
import { isEnableTemplate } from '@/api/modules/setting/template';
|
||||||
|
|
||||||
|
import type { DefinedFieldItem } from '@/models/setting/template';
|
||||||
|
|
||||||
|
import useAppStore from '../app';
|
||||||
|
|
||||||
|
const useTemplateStore = defineStore('template', {
|
||||||
|
persist: true,
|
||||||
|
state: (): { templateStatus: Record<string, boolean>; previewList: DefinedFieldItem[] } => ({
|
||||||
|
templateStatus: {
|
||||||
|
FUNCTIONAL: false,
|
||||||
|
API: false,
|
||||||
|
UI: false,
|
||||||
|
TEST_PLAN: false,
|
||||||
|
BUG: false,
|
||||||
|
},
|
||||||
|
previewList: [],
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
// 模板列表的状态
|
||||||
|
setStatus() {
|
||||||
|
// 需要调整接口
|
||||||
|
// const appStore = useAppStore();
|
||||||
|
// Object.keys(this.templateStatus).forEach(async (item) => {
|
||||||
|
// const sceneStatus = await isEnableTemplate(appStore.currentOrgId, item);
|
||||||
|
// this.templateStatus[item] = sceneStatus;
|
||||||
|
// });
|
||||||
|
},
|
||||||
|
// 预览存储表数据
|
||||||
|
setPreviewHandler(filedData: DefinedFieldItem[]) {
|
||||||
|
this.previewList = filedData;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default useTemplateStore;
|
|
@ -1,9 +1,165 @@
|
||||||
<template>
|
<template>
|
||||||
<div>项目版本 waiting for development </div>
|
<div>项目版本 waiting for development </div>
|
||||||
|
<MsFormCreate :form-rule="formRules" :form-create-key="FormCreateKeyEnum.ORGANIZE_TEMPLATE" />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import MsFormCreate from '@/components/pure/ms-form-create/form-create.vue';
|
||||||
|
import type { FormItem } from '@/components/pure/ms-form-create/types';
|
||||||
|
|
||||||
|
import { FormCreateKeyEnum } from '@/enums/formCreateEnum';
|
||||||
|
|
||||||
|
const formRules = ref<FormItem[]>([
|
||||||
|
{
|
||||||
|
type: 'INPUT',
|
||||||
|
name: 'name',
|
||||||
|
label: '姓名',
|
||||||
|
value: '',
|
||||||
|
subDesc: '请输入姓名',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'MULTIPLE_SELECT',
|
||||||
|
name: 'gender',
|
||||||
|
label: '性别',
|
||||||
|
value: [],
|
||||||
|
subDesc: '请选择性别',
|
||||||
|
optionMethod: 'getGenderOptions',
|
||||||
|
inputSearch: true,
|
||||||
|
required: true,
|
||||||
|
couplingConfig: [
|
||||||
|
{
|
||||||
|
type: 'initOptions',
|
||||||
|
cascade: 'name',
|
||||||
|
matchRule: 'includes',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: '1',
|
||||||
|
text: '单选',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '2',
|
||||||
|
text: '多选',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'INPUT',
|
||||||
|
name: 'member',
|
||||||
|
label: '成员',
|
||||||
|
value: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'MULTIPLE_MEMBER',
|
||||||
|
name: 'multiple_member',
|
||||||
|
label: '多选成员',
|
||||||
|
value: [],
|
||||||
|
required: true,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: '1',
|
||||||
|
text: '单选',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '2',
|
||||||
|
text: '多选',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'INT',
|
||||||
|
name: 'birthday',
|
||||||
|
label: '出生日期',
|
||||||
|
value: 0,
|
||||||
|
subDesc: '请选择出生日期',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'DATE',
|
||||||
|
name: 'birthday',
|
||||||
|
label: '出生日期',
|
||||||
|
value: '',
|
||||||
|
subDesc: '请选择出生日期',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'MULTIPLE_MEMBER',
|
||||||
|
name: 'radio',
|
||||||
|
label: '单选',
|
||||||
|
value: '',
|
||||||
|
subDesc: '请选择出生日期',
|
||||||
|
inputSearch: true,
|
||||||
|
required: true,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: '1',
|
||||||
|
text: '单选',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '2',
|
||||||
|
text: '多选',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
couplingConfig: [
|
||||||
|
{
|
||||||
|
type: 'initOptions',
|
||||||
|
cascade: 'member',
|
||||||
|
matchRule: 'includes',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECT',
|
||||||
|
name: 'selectName',
|
||||||
|
label: '单选',
|
||||||
|
value: '',
|
||||||
|
subDesc: '请选择出生日期',
|
||||||
|
inputSearch: true,
|
||||||
|
required: true,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: '1',
|
||||||
|
text: '单选',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '2',
|
||||||
|
text: '多选',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
couplingConfig: [
|
||||||
|
{
|
||||||
|
type: 'initOptions',
|
||||||
|
cascade: 'member',
|
||||||
|
matchRule: 'includes',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const options = ref({
|
||||||
|
resetBtn: false,
|
||||||
|
submitBtn: false,
|
||||||
|
on: false,
|
||||||
|
form: {
|
||||||
|
layout: 'vertical',
|
||||||
|
labelAlign: 'left',
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
gutter: 0,
|
||||||
|
},
|
||||||
|
wrap: {
|
||||||
|
'asterisk-position': 'end',
|
||||||
|
'validate-trigger': ['change'],
|
||||||
|
},
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -0,0 +1,295 @@
|
||||||
|
<template>
|
||||||
|
<MsDrawer
|
||||||
|
v-model:visible="showAddDrawer"
|
||||||
|
:title="t('system.orgTemplate.createField')"
|
||||||
|
:ok-text="t('system.orgTemplate.save')"
|
||||||
|
:ok-loading="drawerLoading"
|
||||||
|
:width="800"
|
||||||
|
unmount-on-close
|
||||||
|
:show-continue="false"
|
||||||
|
@confirm="handleDrawerConfirm"
|
||||||
|
@cancel="handleDrawerCancel"
|
||||||
|
>
|
||||||
|
<div class="panel-wrapper">
|
||||||
|
<div class="inner-wrapper">
|
||||||
|
<div class="optional-field">
|
||||||
|
<div class="optional-header">
|
||||||
|
<div class="font-medium">{{ t('system.orgTemplate.optionalField') }}</div>
|
||||||
|
<a-checkbox :model-value="checkedAll" :indeterminate="indeterminate" @change="handleChangeAll">
|
||||||
|
<span class="font-medium text-[var(--color-text-3)]">{{ t('system.orgTemplate.selectAll') }}</span>
|
||||||
|
</a-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="optional-panel p-4">
|
||||||
|
<div class="mb-2 font-medium text-[var(--color-text-3)]">{{ t('system.orgTemplate.systemField') }}</div>
|
||||||
|
<div>
|
||||||
|
<a-checkbox-group v-model="selectSystemData">
|
||||||
|
<a-grid :cols="4" :col-gap="16" :row-gap="4">
|
||||||
|
<a-grid-item v-for="field in systemField" :key="field.id">
|
||||||
|
<a-checkbox :value="field.id" :disabled="field.internal"
|
||||||
|
><a-tooltip :content="field.name">
|
||||||
|
<div class="checkbox">{{ field.name }}</div></a-tooltip
|
||||||
|
></a-checkbox
|
||||||
|
>
|
||||||
|
</a-grid-item>
|
||||||
|
</a-grid>
|
||||||
|
</a-checkbox-group>
|
||||||
|
</div>
|
||||||
|
<div class="my-2 mt-8 font-medium text-[var(--color-text-3)]">{{
|
||||||
|
t('system.orgTemplate.customField')
|
||||||
|
}}</div>
|
||||||
|
<a-checkbox-group v-model="selectCustomField">
|
||||||
|
<a-grid :cols="4" :col-gap="16" :row-gap="4">
|
||||||
|
<a-grid-item v-for="field in customField" :key="field.id">
|
||||||
|
<a-checkbox :value="field.id"
|
||||||
|
><a-tooltip :content="field.name">
|
||||||
|
<div class="checkbox">{{ field.name }}</div></a-tooltip
|
||||||
|
></a-checkbox
|
||||||
|
>
|
||||||
|
</a-grid-item>
|
||||||
|
</a-grid>
|
||||||
|
</a-checkbox-group>
|
||||||
|
<EditFieldDrawer ref="fieldDrawerRef" v-model:visible="showFieldDrawer" @success="okHandler" />
|
||||||
|
<a-button class="mt-1 px-0" type="text" @click="createField">
|
||||||
|
<template #icon>
|
||||||
|
<icon-plus class="text-[14px]" />
|
||||||
|
</template>
|
||||||
|
{{ t('system.orgTemplate.addField') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="selected-field">
|
||||||
|
<div class="optional-header">
|
||||||
|
<div class="font-medium">{{ t('system.orgTemplate.selectedField') }}</div>
|
||||||
|
<MsButton @click="clearHandler">{{ t('system.orgTemplate.clear') }}</MsButton>
|
||||||
|
</div>
|
||||||
|
<div class="selected-list p-4">
|
||||||
|
<MsList
|
||||||
|
:data="selectedFields"
|
||||||
|
:bordered="false"
|
||||||
|
:split="false"
|
||||||
|
item-border
|
||||||
|
no-hover
|
||||||
|
:virtual-list-props="{
|
||||||
|
height: 'calc(100vh - 226px)',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #item="{ item }">
|
||||||
|
<div class="selected-item">
|
||||||
|
<a-tooltip :content="item.name">
|
||||||
|
<span class="one-line-text w-[270px]">{{ item.name }}</span>
|
||||||
|
</a-tooltip>
|
||||||
|
<icon-close
|
||||||
|
v-if="!item.internal"
|
||||||
|
:style="{ 'font-size': '14px' }"
|
||||||
|
class="cursor-pointer text-[var(--color-text-3)]"
|
||||||
|
@click="removeSelectedField(item.id)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MsList>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MsDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||||
|
import MsList from '@/components/pure/ms-list/index.vue';
|
||||||
|
import EditFieldDrawer from './editFieldDrawer.vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
import type { DefinedFieldItem } from '@/models/setting/template';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const showAddDrawer = ref<boolean>(false);
|
||||||
|
const drawerLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
visible: boolean;
|
||||||
|
selectedData: DefinedFieldItem[]; // 已选字段
|
||||||
|
systemData: DefinedFieldItem[]; // 所有系统字段
|
||||||
|
customData: DefinedFieldItem[]; // 所有自定义字段
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['confirm', 'update:visible', 'update']);
|
||||||
|
|
||||||
|
// 模板系统字段
|
||||||
|
const systemField = ref<DefinedFieldItem[]>([]);
|
||||||
|
// 自定义字段
|
||||||
|
const customField = ref<DefinedFieldItem[]>([]);
|
||||||
|
// 所有模板总字段
|
||||||
|
const totalTemplateField = ref<DefinedFieldItem[]>([]);
|
||||||
|
// 可选系统字段
|
||||||
|
const selectSystemData = ref<string[]>([]);
|
||||||
|
// 可选自定义字段
|
||||||
|
const selectCustomField = ref<string[]>([]);
|
||||||
|
// 半选状态
|
||||||
|
const indeterminate = ref<boolean>(false);
|
||||||
|
|
||||||
|
const checkedAll = ref<boolean>(false);
|
||||||
|
|
||||||
|
// 全选
|
||||||
|
const handleChangeAll = (value: any) => {
|
||||||
|
indeterminate.value = false;
|
||||||
|
if (value) {
|
||||||
|
checkedAll.value = true;
|
||||||
|
selectSystemData.value = systemField.value.map((item) => item.id);
|
||||||
|
selectCustomField.value = customField.value.map((item) => item.id);
|
||||||
|
} else {
|
||||||
|
checkedAll.value = false;
|
||||||
|
selectSystemData.value = [];
|
||||||
|
selectCustomField.value = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 已选择列表
|
||||||
|
const selectedFields = ref<DefinedFieldItem[]>([]);
|
||||||
|
const getSelectedField = () => {
|
||||||
|
const totalSelectIds = [...selectSystemData.value, ...selectCustomField.value];
|
||||||
|
selectedFields.value = totalTemplateField.value.filter((item) => totalSelectIds.indexOf(item.id) > -1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理全选&半选&未选
|
||||||
|
const isCheckedAll = () => {
|
||||||
|
totalTemplateField.value = [...props.systemData, ...props.customData];
|
||||||
|
systemField.value = props.systemData;
|
||||||
|
customField.value = props.customData;
|
||||||
|
const systemAll = selectSystemData.value.length === systemField.value.length;
|
||||||
|
const customAll = selectCustomField.value.length === customField.value.length;
|
||||||
|
if (systemAll && customAll) {
|
||||||
|
checkedAll.value = true;
|
||||||
|
indeterminate.value = false;
|
||||||
|
} else if (selectCustomField.value.length === 0 || selectCustomField.value.length === 0) {
|
||||||
|
checkedAll.value = false;
|
||||||
|
indeterminate.value = false;
|
||||||
|
} else {
|
||||||
|
checkedAll.value = false;
|
||||||
|
indeterminate.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
isCheckedAll();
|
||||||
|
// 已经选择的列表
|
||||||
|
getSelectedField();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 移除已选择字段
|
||||||
|
const removeSelectedField = (id: string) => {
|
||||||
|
selectSystemData.value = selectSystemData.value.filter((item) => item !== id);
|
||||||
|
selectCustomField.value = selectCustomField.value.filter((item) => item !== id);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 新增字段
|
||||||
|
const showFieldDrawer = ref<boolean>(false);
|
||||||
|
const createField = () => {
|
||||||
|
showFieldDrawer.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清空
|
||||||
|
const clearHandler = () => {
|
||||||
|
selectSystemData.value = [];
|
||||||
|
selectCustomField.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrawerCancel = () => {
|
||||||
|
showAddDrawer.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrawerConfirm = () => {
|
||||||
|
emit('confirm', selectedFields.value);
|
||||||
|
showAddDrawer.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const okHandler = () => {
|
||||||
|
emit('update');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 针对外部操作的进行回显
|
||||||
|
const showSelectField = () => {
|
||||||
|
const selectIds = props.selectedData.map((item) => item.id);
|
||||||
|
// 回显系统字段
|
||||||
|
selectSystemData.value = systemField.value.filter((item) => selectIds.indexOf(item.id) > -1).map((item) => item.id);
|
||||||
|
// 回显自定义字段
|
||||||
|
selectCustomField.value = customField.value
|
||||||
|
.filter((item) => selectIds.indexOf(item.id) > -1)
|
||||||
|
.map((item) => item.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => showAddDrawer.value,
|
||||||
|
(val) => {
|
||||||
|
emit('update:visible', val);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(val) => {
|
||||||
|
showAddDrawer.value = val;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
removeSelectedField,
|
||||||
|
showSelectField,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.panel-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
.inner-wrapper {
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 1px solid var(--color-text-n8);
|
||||||
|
// 可选字段
|
||||||
|
.optional-field {
|
||||||
|
flex-grow: 1;
|
||||||
|
height: 100%;
|
||||||
|
border-right: 1px solid var(--color-text-n8);
|
||||||
|
.optional-header {
|
||||||
|
padding: 0 16px;
|
||||||
|
height: 54px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
background: var(--color-text-n9);
|
||||||
|
@apply flex items-center justify-between;
|
||||||
|
}
|
||||||
|
.optional-panel {
|
||||||
|
.checkbox {
|
||||||
|
width: 74px;
|
||||||
|
white-space: nowrap;
|
||||||
|
@apply overflow-hidden text-ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 已选字段
|
||||||
|
.selected-field {
|
||||||
|
width: 272px;
|
||||||
|
.optional-header {
|
||||||
|
padding: 0 16px;
|
||||||
|
height: 54px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
background: var(--color-text-n9);
|
||||||
|
@apply flex items-center justify-between;
|
||||||
|
}
|
||||||
|
.selected-list {
|
||||||
|
.selected-item {
|
||||||
|
height: 36px;
|
||||||
|
background: var(--color-bg-3);
|
||||||
|
@apply mb-2 flex items-center justify-between rounded px-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -22,14 +22,14 @@
|
||||||
<a-input
|
<a-input
|
||||||
v-model:model-value="fieldForm.name"
|
v-model:model-value="fieldForm.name"
|
||||||
:placeholder="t('system.orgTemplate.fieldNamePlaceholder')"
|
:placeholder="t('system.orgTemplate.fieldNamePlaceholder')"
|
||||||
:max-length="250"
|
:max-length="255"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
></a-input>
|
></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item field="remark" :label="t('system.orgTemplate.description')" asterisk-position="end">
|
<a-form-item field="remark" :label="t('system.orgTemplate.description')" asterisk-position="end">
|
||||||
<a-textarea
|
<a-textarea
|
||||||
v-model="fieldForm.remark"
|
v-model="fieldForm.remark"
|
||||||
:max-length="250"
|
:max-length="255"
|
||||||
:placeholder="t('system.orgTemplate.resDescription')"
|
:placeholder="t('system.orgTemplate.resDescription')"
|
||||||
:auto-size="{
|
:auto-size="{
|
||||||
maxRows: 1,
|
maxRows: 1,
|
||||||
|
@ -42,11 +42,12 @@
|
||||||
class="w-[260px]"
|
class="w-[260px]"
|
||||||
:placeholder="t('system.orgTemplate.fieldTypePlaceholder')"
|
:placeholder="t('system.orgTemplate.fieldTypePlaceholder')"
|
||||||
allow-clear
|
allow-clear
|
||||||
|
:disabled="isEdit"
|
||||||
@change="fieldChangeHandler"
|
@change="fieldChangeHandler"
|
||||||
>
|
>
|
||||||
<a-option v-for="item of fieldOptions" :key="item.value" :value="item.id">
|
<a-option v-for="item of fieldOptions" :key="item.key" :value="item.key">
|
||||||
<div class="flex items-center"
|
<div class="flex items-center"
|
||||||
><MsIcon :type="item.value" class="mx-2" /> <span>{{ item.label }}</span></div
|
><MsIcon :type="item.iconName" class="mx-2" /> <span>{{ item.label }}</span></div
|
||||||
>
|
>
|
||||||
</a-option>
|
</a-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
|
@ -81,9 +82,7 @@
|
||||||
<a-form-item
|
<a-form-item
|
||||||
v-if="showDateOrNumber"
|
v-if="showDateOrNumber"
|
||||||
field="selectFormat"
|
field="selectFormat"
|
||||||
:label="
|
:label="fieldType === 'NUMBER' ? t('system.orgTemplate.numberFormat') : t('system.orgTemplate.dateFormat')"
|
||||||
fieldForm.type === 'NUMBER' ? t('system.orgTemplate.numberFormat') : t('system.orgTemplate.dateFormat')
|
|
||||||
"
|
|
||||||
asterisk-position="end"
|
asterisk-position="end"
|
||||||
>
|
>
|
||||||
<a-select
|
<a-select
|
||||||
|
@ -91,6 +90,7 @@
|
||||||
class="w-[260px]"
|
class="w-[260px]"
|
||||||
:placeholder="t('system.orgTemplate.formatPlaceholder')"
|
:placeholder="t('system.orgTemplate.formatPlaceholder')"
|
||||||
allow-clear
|
allow-clear
|
||||||
|
:disabled="isEdit"
|
||||||
>
|
>
|
||||||
<a-option v-for="item of showDateOrNumber" :key="item.value" :value="item.value">
|
<a-option v-for="item of showDateOrNumber" :key="item.value" :value="item.value">
|
||||||
<div class="flex items-center">{{ item.label }}</div>
|
<div class="flex items-center">{{ item.label }}</div>
|
||||||
|
@ -108,6 +108,7 @@
|
||||||
import { FormInstance, Message, ValidatedError } from '@arco-design/web-vue';
|
import { FormInstance, Message, ValidatedError } from '@arco-design/web-vue';
|
||||||
|
|
||||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||||
|
import type { FormItemType } from '@/components/pure/ms-form-create/types';
|
||||||
import MsBatchForm from '@/components/business/ms-batch-form/index.vue';
|
import MsBatchForm from '@/components/business/ms-batch-form/index.vue';
|
||||||
import type { FormItemModel, MsBatchFormInstance } from '@/components/business/ms-batch-form/types';
|
import type { FormItemModel, MsBatchFormInstance } from '@/components/business/ms-batch-form/types';
|
||||||
|
|
||||||
|
@ -116,10 +117,9 @@
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
import { getGenerateId } from '@/utils';
|
import { getGenerateId } from '@/utils';
|
||||||
|
|
||||||
import type { AddOrUpdateField } from '@/models/setting/template';
|
import type { AddOrUpdateField, fieldIconAndNameModal } from '@/models/setting/template';
|
||||||
import { TemplateIconEnum } from '@/enums/templateEnum';
|
|
||||||
|
|
||||||
import { getFieldType } from './fieldSetting';
|
import { fieldIconAndName, getFieldType } from './fieldSetting';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -136,7 +136,7 @@
|
||||||
const fieldFormRef = ref<FormInstance>();
|
const fieldFormRef = ref<FormInstance>();
|
||||||
const initFieldForm: AddOrUpdateField = {
|
const initFieldForm: AddOrUpdateField = {
|
||||||
name: '',
|
name: '',
|
||||||
type: '',
|
type: 'INPUT',
|
||||||
remark: '',
|
remark: '',
|
||||||
scopeId: '',
|
scopeId: '',
|
||||||
scene: 'FUNCTIONAL',
|
scene: 'FUNCTIONAL',
|
||||||
|
@ -144,19 +144,19 @@
|
||||||
};
|
};
|
||||||
const fieldForm = ref<AddOrUpdateField>({ ...initFieldForm });
|
const fieldForm = ref<AddOrUpdateField>({ ...initFieldForm });
|
||||||
const isEdit = computed(() => !!fieldForm.value.id);
|
const isEdit = computed(() => !!fieldForm.value.id);
|
||||||
const selectFormat = ref(''); // 选择格式
|
const selectFormat = ref<FormItemType>(); // 选择格式
|
||||||
const isMultipleSelectMember = ref<boolean | undefined>(false); // 成员多选
|
const isMultipleSelectMember = ref<boolean | undefined>(false); // 成员多选
|
||||||
const fieldType = ref(''); // 整体字段类型
|
const fieldType = ref<FormItemType>(); // 整体字段类型
|
||||||
|
|
||||||
// 是否展示选项添加面板
|
// 是否展示选项添加面板
|
||||||
const showOptionsSelect = computed(() => {
|
const showOptionsSelect = computed(() => {
|
||||||
const showOptionsType = ['RADIO', 'CHECKBOX', 'SELECT', 'MULTIPLE_SELECT'];
|
const showOptionsType: FormItemType[] = ['RADIO', 'CHECKBOX', 'SELECT', 'MULTIPLE_SELECT'];
|
||||||
return showOptionsType.includes(fieldType.value);
|
return showOptionsType.includes(fieldType.value as FormItemType);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 是否展示日期或数值
|
// 是否展示日期或数值
|
||||||
const showDateOrNumber = computed(() => {
|
const showDateOrNumber = computed(() => {
|
||||||
return getFieldType(fieldType.value);
|
if (fieldType.value) return getFieldType(fieldType.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 批量表单-1.仅选项情况
|
// 批量表单-1.仅选项情况
|
||||||
|
@ -174,9 +174,9 @@
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
fieldForm.value = { ...initFieldForm };
|
fieldForm.value = { ...initFieldForm };
|
||||||
selectFormat.value = '';
|
selectFormat.value = undefined;
|
||||||
isMultipleSelectMember.value = false;
|
isMultipleSelectMember.value = false;
|
||||||
fieldType.value = '';
|
fieldType.value = undefined;
|
||||||
batchFormRef.value?.resetForm();
|
batchFormRef.value?.resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -190,20 +190,23 @@
|
||||||
const confirmHandler = async (isContinue: boolean) => {
|
const confirmHandler = async (isContinue: boolean) => {
|
||||||
try {
|
try {
|
||||||
drawerLoading.value = true;
|
drawerLoading.value = true;
|
||||||
|
if (fieldType.value) {
|
||||||
|
fieldForm.value.type = fieldType.value;
|
||||||
|
}
|
||||||
|
|
||||||
fieldForm.value.scene = route.query.type;
|
fieldForm.value.scene = route.query.type;
|
||||||
fieldForm.value = {
|
fieldForm.value.scopeId = appStore.currentOrgId;
|
||||||
...fieldForm.value,
|
|
||||||
scopeId: appStore.currentOrgId,
|
|
||||||
type: fieldType.value,
|
|
||||||
};
|
|
||||||
// 如果选择是日期或者数值
|
// 如果选择是日期或者数值
|
||||||
if (selectFormat.value) {
|
if (selectFormat.value) {
|
||||||
fieldForm.value.type = selectFormat.value;
|
fieldForm.value.type = selectFormat.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果选择是成员(单选||多选)
|
// 如果选择是成员(单选||多选)
|
||||||
if (isMultipleSelectMember.value) {
|
if (isMultipleSelectMember.value) {
|
||||||
fieldForm.value.type = isMultipleSelectMember.value ? 'MULTIPLE_MEMBER' : 'MEMBER';
|
fieldForm.value.type = isMultipleSelectMember.value ? 'MULTIPLE_MEMBER' : 'MEMBER';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果选择是日期或者是数值
|
// 如果选择是日期或者是数值
|
||||||
if (selectFormat.value) {
|
if (selectFormat.value) {
|
||||||
fieldForm.value.type = selectFormat.value;
|
fieldForm.value.type = selectFormat.value;
|
||||||
|
@ -212,6 +215,7 @@
|
||||||
// 处理参数
|
// 处理参数
|
||||||
const { id, name, options, scopeId, scene, type, remark } = fieldForm.value;
|
const { id, name, options, scopeId, scene, type, remark } = fieldForm.value;
|
||||||
const params: AddOrUpdateField = { name, options, scopeId, scene, type, remark };
|
const params: AddOrUpdateField = { name, options, scopeId, scene, type, remark };
|
||||||
|
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
params.id = id;
|
params.id = id;
|
||||||
}
|
}
|
||||||
|
@ -247,59 +251,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
// 字段类型列表选项
|
// 字段类型列表选项
|
||||||
const fieldOptions = ref([
|
const fieldOptions = ref<fieldIconAndNameModal[]>([]);
|
||||||
{
|
|
||||||
id: 'TEXTAREA',
|
|
||||||
label: t('system.orgTemplate.textarea'),
|
|
||||||
value: TemplateIconEnum.TEXTAREA,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'INPUT',
|
|
||||||
label: t('system.orgTemplate.input'),
|
|
||||||
value: TemplateIconEnum.INPUT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'RADIO',
|
|
||||||
label: t('system.orgTemplate.radio'),
|
|
||||||
value: TemplateIconEnum.RADIO,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'CHECKBOX',
|
|
||||||
label: t('system.orgTemplate.checkbox'),
|
|
||||||
value: TemplateIconEnum.CHECKBOX,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'SELECT',
|
|
||||||
label: t('system.orgTemplate.select'),
|
|
||||||
value: TemplateIconEnum.SELECT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'MULTIPLE_SELECT',
|
|
||||||
label: t('system.orgTemplate.multipleSelect'),
|
|
||||||
value: TemplateIconEnum.MULTIPLE_SELECT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'MEMBER',
|
|
||||||
label: t('system.orgTemplate.member'),
|
|
||||||
value: TemplateIconEnum.MEMBER,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'DATE',
|
|
||||||
label: t('system.orgTemplate.date'),
|
|
||||||
value: TemplateIconEnum.DATE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'NUMBER',
|
|
||||||
label: t('system.orgTemplate.number'),
|
|
||||||
value: TemplateIconEnum.NUMBER,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'MULTIPLE_INPUT',
|
|
||||||
label: t('system.orgTemplate.multipleInput'),
|
|
||||||
value: TemplateIconEnum.MULTIPLE_INPUT,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const fieldDefaultValues = ref([]);
|
const fieldDefaultValues = ref([]);
|
||||||
|
|
||||||
// 获取字段选项详情
|
// 获取字段选项详情
|
||||||
|
@ -317,7 +269,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理特殊情况编辑回显
|
// 处理特殊情况编辑回显
|
||||||
const getSpecialHandler = (itemType: string): string => {
|
const getSpecialHandler = (itemType: FormItemType): FormItemType => {
|
||||||
switch (itemType) {
|
switch (itemType) {
|
||||||
case 'INT':
|
case 'INT':
|
||||||
selectFormat.value = itemType;
|
selectFormat.value = itemType;
|
||||||
|
@ -336,7 +288,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
// 编辑
|
// 编辑
|
||||||
const isEditHandler = (item: AddOrUpdateField) => {
|
const editHandler = (item: AddOrUpdateField) => {
|
||||||
showDrawer.value = true;
|
showDrawer.value = true;
|
||||||
isMultipleSelectMember.value = item.type === 'MULTIPLE_MEMBER';
|
isMultipleSelectMember.value = item.type === 'MULTIPLE_MEMBER';
|
||||||
if (isEdit && item.id) {
|
if (isEdit && item.id) {
|
||||||
|
@ -345,7 +297,7 @@
|
||||||
...item,
|
...item,
|
||||||
type: getSpecialHandler(item.type),
|
type: getSpecialHandler(item.type),
|
||||||
};
|
};
|
||||||
fieldType.value = getSpecialHandler(item.type);
|
fieldType.value = fieldForm.value.type;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -368,9 +320,13 @@
|
||||||
showDrawer.value = val;
|
showDrawer.value = val;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
onMounted(() => {
|
||||||
|
const excludeOptions = ['MULTIPLE_MEMBER', 'DATETIME', 'SYSTEM', 'INT', 'FLOAT'];
|
||||||
|
fieldOptions.value = fieldIconAndName.filter((item: any) => excludeOptions.indexOf(item.key) < 0);
|
||||||
|
});
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
isEditHandler,
|
editHandler,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,18 @@
|
||||||
import { ref } from 'vue';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
import type { FormItemType } from '@/components/pure/ms-form-create/types';
|
||||||
|
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useTemplateStore from '@/store/modules/setting/template';
|
||||||
|
|
||||||
|
import type { fieldIconAndNameModal } from '@/models/setting/template';
|
||||||
|
import { TemplateCardEnum, TemplateIconEnum } from '@/enums/templateEnum';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const templateStore = useTemplateStore();
|
||||||
|
|
||||||
// 字段类型-日期
|
// 字段类型-日期
|
||||||
const dateOptions = ref([
|
const dateOptions = [
|
||||||
{
|
{
|
||||||
label: dayjs().format('YYYY/MM/DD'),
|
label: dayjs().format('YYYY/MM/DD'),
|
||||||
value: 'DATE',
|
value: 'DATE',
|
||||||
|
@ -11,10 +21,10 @@ const dateOptions = ref([
|
||||||
label: dayjs().format('YYYY/MM/DD HH:mm:ss'),
|
label: dayjs().format('YYYY/MM/DD HH:mm:ss'),
|
||||||
value: 'DATETIME',
|
value: 'DATETIME',
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
|
||||||
// 字段类型- 数字
|
// 字段类型- 数字
|
||||||
const numberTypeOptions = ref([
|
const numberTypeOptions = [
|
||||||
{
|
{
|
||||||
label: '整数',
|
label: '整数',
|
||||||
value: 'INT',
|
value: 'INT',
|
||||||
|
@ -23,17 +33,141 @@ const numberTypeOptions = ref([
|
||||||
label: '保留小数',
|
label: '保留小数',
|
||||||
value: 'FLOAT',
|
value: 'FLOAT',
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
|
||||||
export const getFieldType = (selectFieldType: string) => {
|
// 获取字段类型是数值 || 日期
|
||||||
|
export const getFieldType = (selectFieldType: FormItemType) => {
|
||||||
switch (selectFieldType) {
|
switch (selectFieldType) {
|
||||||
case 'DATE':
|
case 'DATE':
|
||||||
return dateOptions.value;
|
return dateOptions;
|
||||||
case 'NUMBER':
|
case 'NUMBER':
|
||||||
return numberTypeOptions.value;
|
return numberTypeOptions;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 模板列表Icon
|
||||||
|
export const cardList = [
|
||||||
|
{
|
||||||
|
id: 1001,
|
||||||
|
key: 'FUNCTIONAL',
|
||||||
|
value: TemplateCardEnum.FUNCTIONAL,
|
||||||
|
name: t('system.orgTemplate.caseTemplates'),
|
||||||
|
enable: templateStore.templateStatus.FUNCTIONAL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1002,
|
||||||
|
key: 'API',
|
||||||
|
value: TemplateCardEnum.API,
|
||||||
|
name: t('system.orgTemplate.APITemplates'),
|
||||||
|
enable: templateStore.templateStatus.API,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1003,
|
||||||
|
key: 'UI',
|
||||||
|
value: TemplateCardEnum.UI,
|
||||||
|
name: t('system.orgTemplate.UITemplates'),
|
||||||
|
enable: templateStore.templateStatus.UI,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1004,
|
||||||
|
key: 'TEST_PLAN',
|
||||||
|
value: TemplateCardEnum.TEST_PLAN,
|
||||||
|
name: t('system.orgTemplate.testPlanTemplates'),
|
||||||
|
enable: templateStore.templateStatus.TEST_PLAN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1005,
|
||||||
|
key: 'BUG',
|
||||||
|
value: TemplateCardEnum.BUG,
|
||||||
|
name: t('system.orgTemplate.defectTemplates'),
|
||||||
|
enable: templateStore.templateStatus.BUG,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// table名称展示图标类型表格展示类型
|
||||||
|
export const fieldIconAndName: fieldIconAndNameModal[] = [
|
||||||
|
{
|
||||||
|
key: 'INPUT',
|
||||||
|
iconName: TemplateIconEnum.INPUT,
|
||||||
|
label: t('system.orgTemplate.input'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'TEXTAREA',
|
||||||
|
iconName: TemplateIconEnum.TEXTAREA,
|
||||||
|
label: t('system.orgTemplate.textarea'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SELECT',
|
||||||
|
iconName: TemplateIconEnum.SELECT,
|
||||||
|
label: t('system.orgTemplate.select'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'MULTIPLE_SELECT',
|
||||||
|
iconName: TemplateIconEnum.MULTIPLE_SELECT,
|
||||||
|
label: t('system.orgTemplate.multipleSelect'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'RADIO',
|
||||||
|
iconName: TemplateIconEnum.RADIO,
|
||||||
|
label: t('system.orgTemplate.radio'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'CHECKBOX',
|
||||||
|
iconName: TemplateIconEnum.CHECKBOX,
|
||||||
|
label: t('system.orgTemplate.checkbox'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'MEMBER',
|
||||||
|
iconName: TemplateIconEnum.MEMBER,
|
||||||
|
label: t('system.orgTemplate.member'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'MULTIPLE_MEMBER',
|
||||||
|
iconName: TemplateIconEnum.MULTIPLE_MEMBER,
|
||||||
|
label: t('system.orgTemplate.multipleMember'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'DATE',
|
||||||
|
iconName: TemplateIconEnum.DATE,
|
||||||
|
label: t('system.orgTemplate.date'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'DATETIME',
|
||||||
|
iconName: TemplateIconEnum.DATETIME,
|
||||||
|
label: t('system.orgTemplate.dateTime'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'NUMBER',
|
||||||
|
iconName: TemplateIconEnum.NUMBER,
|
||||||
|
label: t('system.orgTemplate.number'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'INT',
|
||||||
|
iconName: TemplateIconEnum.INT,
|
||||||
|
label: t('system.orgTemplate.number'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'FLOAT',
|
||||||
|
iconName: TemplateIconEnum.FLOAT,
|
||||||
|
label: t('system.orgTemplate.number'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'MULTIPLE_INPUT',
|
||||||
|
iconName: TemplateIconEnum.MULTIPLE_INPUT,
|
||||||
|
label: t('system.orgTemplate.multipleInput'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'SYSTEM',
|
||||||
|
iconName: TemplateIconEnum.SYSTEM,
|
||||||
|
label: '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 获取图标类型
|
||||||
|
export const getIconType = (iconType: FormItemType) => {
|
||||||
|
return fieldIconAndName.find((item) => item.key === iconType);
|
||||||
|
};
|
||||||
|
|
||||||
export default {};
|
export default {};
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<MsCard :has-breadcrumb="true" simple>
|
<MsCard :has-breadcrumb="true" simple>
|
||||||
<a-alert class="mb-6" type="warning">{{ t('system.orgTemplate.enableDescription') }}</a-alert>
|
<a-alert class="mb-6" :type="isEnable ? 'warning' : 'info'">{{
|
||||||
|
isEnable ? t('system.orgTemplate.enableDescription') : t('system.orgTemplate.fieldLimit')
|
||||||
|
}}</a-alert>
|
||||||
<div class="mb-4 flex items-center justify-between">
|
<div class="mb-4 flex items-center justify-between">
|
||||||
<span v-if="isEnable" class="font-medium">{{ t('system.orgTemplate.fieldList') }}</span>
|
<span v-if="isEnable" class="font-medium">{{ t('system.orgTemplate.fieldList') }}</span>
|
||||||
<a-button v-else type="primary" :disabled="false" @click="fieldHandler('add')">
|
<a-button v-else type="primary" :disabled="totalData.length > 20" @click="fieldHandler('add')">
|
||||||
{{ t('system.orgTemplate.addField') }}
|
{{ t('system.orgTemplate.addField') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-input-search
|
<a-input-search
|
||||||
|
@ -17,13 +19,22 @@
|
||||||
</div>
|
</div>
|
||||||
<MsBaseTable v-bind="propsRes" ref="tableRef" v-on="propsEvent">
|
<MsBaseTable v-bind="propsRes" ref="tableRef" v-on="propsEvent">
|
||||||
<template #name="{ record }">
|
<template #name="{ record }">
|
||||||
<MsIcon v-if="getIconType(record.type).type !== 'system'" :type="getIconType(record.type).iconName" size="16" />
|
<MsIcon v-if="!record.internal" :type="getIconType(record.type)?.iconName || ''" size="16" />
|
||||||
<span class="ml-2">{{ record.name }}</span>
|
<span class="ml-2">{{ record.name }}</span>
|
||||||
<span v-if="record.internal" class="system-flag">{{ t('system.orgTemplate.isSystem') }}</span>
|
<span v-if="record.internal" class="system-flag">{{ t('system.orgTemplate.isSystem') }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record }">
|
||||||
<div class="flex flex-row flex-nowrap">
|
<div class="flex flex-row flex-nowrap">
|
||||||
<MsButton class="!mr-0" @click="fieldHandler('edit', record)">{{ t('system.orgTemplate.edit') }}</MsButton>
|
<MsPopConfirm
|
||||||
|
type="error"
|
||||||
|
:title="t('system.orgTemplate.updateTip', { name: characterLimit(record.name) })"
|
||||||
|
:sub-title-tip="t('system.orgTemplate.updateDescription')"
|
||||||
|
:ok-text="t('system.orgTemplate.confirm')"
|
||||||
|
@confirm="handleOk(record)"
|
||||||
|
>
|
||||||
|
<MsButton class="!mr-0">{{ t('system.orgTemplate.edit') }}</MsButton></MsPopConfirm
|
||||||
|
>
|
||||||
|
|
||||||
<a-divider v-if="!record.internal" direction="vertical" />
|
<a-divider v-if="!record.internal" direction="vertical" />
|
||||||
<MsTableMoreAction
|
<MsTableMoreAction
|
||||||
v-if="!record.internal"
|
v-if="!record.internal"
|
||||||
|
@ -33,7 +44,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #fieldType="{ record }">
|
<template #fieldType="{ record }">
|
||||||
<span>{{ getIconType(record.type)['type'] }}</span>
|
<span>{{ getIconType(record.type)?.label }}</span>
|
||||||
</template>
|
</template>
|
||||||
</MsBaseTable>
|
</MsBaseTable>
|
||||||
<EditFieldDrawer ref="fieldDrawerRef" v-model:visible="showDrawer" @success="successHandler" />
|
<EditFieldDrawer ref="fieldDrawerRef" v-model:visible="showDrawer" @success="successHandler" />
|
||||||
|
@ -41,12 +52,16 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
/**
|
||||||
|
* @description 系统管理-组织-模版-字段列表
|
||||||
|
*/
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||||
|
import MsPopConfirm from '@/components/pure/ms-popconfirm/index.vue';
|
||||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
import useTable from '@/components/pure/ms-table/useTable';
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
|
@ -58,11 +73,15 @@
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
import { useAppStore, useTableStore } from '@/store';
|
import { useAppStore, useTableStore } from '@/store';
|
||||||
|
import useTemplateStore from '@/store/modules/setting/template';
|
||||||
import { characterLimit } from '@/utils';
|
import { characterLimit } from '@/utils';
|
||||||
|
|
||||||
import type { AddOrUpdateField } from '@/models/setting/template';
|
import type { AddOrUpdateField, SeneType } from '@/models/setting/template';
|
||||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
import { TemplateIconEnum } from '@/enums/templateEnum';
|
|
||||||
|
import { cardList, getIconType } from './fieldSetting';
|
||||||
|
|
||||||
|
const templateStore = useTemplateStore();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const tableStore = useTableStore();
|
const tableStore = useTableStore();
|
||||||
|
@ -109,11 +128,10 @@
|
||||||
showDrag: false,
|
showDrag: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
tableStore.initColumn(TableKeyEnum.ORGANIZATION_TEMPLATE_FIELD_SETTING, fieldColumns, 'drawer');
|
||||||
tableStore.initColumn(TableKeyEnum.ORGANIZATION_TEMPLATE, fieldColumns, 'drawer');
|
|
||||||
|
|
||||||
const { propsRes, propsEvent, loadList, setLoadListParams, setProps } = useTable(getFieldList, {
|
const { propsRes, propsEvent, loadList, setLoadListParams, setProps } = useTable(getFieldList, {
|
||||||
tableKey: TableKeyEnum.ORGANIZATION_TEMPLATE,
|
tableKey: TableKeyEnum.ORGANIZATION_TEMPLATE_FIELD_SETTING,
|
||||||
scroll: { x: '1000px' },
|
scroll: { x: '1000px' },
|
||||||
selectable: false,
|
selectable: false,
|
||||||
noDisable: true,
|
noDisable: true,
|
||||||
|
@ -124,73 +142,40 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
const keyword = ref('');
|
const keyword = ref('');
|
||||||
|
const totalData = ref([]);
|
||||||
|
|
||||||
// 查询字段
|
// 查询模板字段
|
||||||
const searchFiled = async () => {
|
const searchFiled = async () => {
|
||||||
try {
|
try {
|
||||||
const totalData = await getFieldList({ organizationId: currentOrd, scene: route.query.type });
|
totalData.value = await getFieldList({ organizationId: currentOrd, scene: route.query.type });
|
||||||
const filterData = totalData.filter((item: AddOrUpdateField) => item.name.includes(keyword.value));
|
const filterData = totalData.value.filter((item: AddOrUpdateField) => item.name.includes(keyword.value));
|
||||||
setProps({ data: filterData });
|
setProps({ data: filterData });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const scene = ref<SeneType>('');
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
const scene = route.query.type;
|
scene.value = route.query.type;
|
||||||
setLoadListParams({ organizationId: currentOrd, scene });
|
setLoadListParams({ organizationId: currentOrd, scene });
|
||||||
await loadList();
|
await loadList();
|
||||||
};
|
};
|
||||||
|
|
||||||
const tableRef = ref();
|
const tableRef = ref();
|
||||||
const isEnable = ref<boolean>(false); // 开始默认未启用
|
const isEnable = ref<boolean>(templateStore.templateStatus[scene.value as string]); // 开始默认未启用
|
||||||
|
|
||||||
// 切换模版是否启用展示操作列
|
// 切换模版是否启用展示操作列
|
||||||
const isEnableOperation = () => {
|
const isEnableOperation = () => {
|
||||||
if (isEnable.value) {
|
if (isEnable.value) {
|
||||||
const noOperationColumn = fieldColumns.slice(0, -1);
|
const noOperationColumn = fieldColumns.slice(0, -1);
|
||||||
tableStore.setColumns(TableKeyEnum.ORGANIZATION_TEMPLATE, noOperationColumn, 'drawer');
|
tableStore.setColumns(TableKeyEnum.ORGANIZATION_TEMPLATE_FIELD_SETTING, noOperationColumn, 'drawer');
|
||||||
tableRef.value.initColumn();
|
tableRef.value.initColumn();
|
||||||
} else {
|
} else {
|
||||||
tableStore.setColumns(TableKeyEnum.ORGANIZATION_TEMPLATE, fieldColumns, 'drawer');
|
tableStore.setColumns(TableKeyEnum.ORGANIZATION_TEMPLATE_FIELD_SETTING, fieldColumns, 'drawer');
|
||||||
tableRef.value.initColumn();
|
tableRef.value.initColumn();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取当前字段类型
|
|
||||||
const getIconType = (iconType: string) => {
|
|
||||||
switch (iconType) {
|
|
||||||
case 'INPUT':
|
|
||||||
return { iconName: TemplateIconEnum.INPUT, type: t('system.orgTemplate.input') };
|
|
||||||
case 'TEXTAREA':
|
|
||||||
return { iconName: TemplateIconEnum.TEXTAREA, type: t('system.orgTemplate.textarea') };
|
|
||||||
case 'SELECT':
|
|
||||||
return { iconName: TemplateIconEnum.SELECT, type: t('system.orgTemplate.select') };
|
|
||||||
case 'MULTIPLE_SELECT':
|
|
||||||
return { iconName: TemplateIconEnum.MULTIPLE_SELECT, type: t('system.orgTemplate.multipleSelect') };
|
|
||||||
case 'RADIO':
|
|
||||||
return { iconName: TemplateIconEnum.RADIO, type: t('system.orgTemplate.radio') };
|
|
||||||
case 'CHECKBOX':
|
|
||||||
return { iconName: TemplateIconEnum.CHECKBOX, type: t('system.orgTemplate.checkbox') };
|
|
||||||
case 'MEMBER':
|
|
||||||
return { iconName: TemplateIconEnum.MEMBER, type: t('system.orgTemplate.member') };
|
|
||||||
case 'MULTIPLE_MEMBER':
|
|
||||||
return { iconName: TemplateIconEnum.MULTIPLE_MEMBER, type: t('system.orgTemplate.multipleMember') };
|
|
||||||
case 'DATE':
|
|
||||||
return { iconName: TemplateIconEnum.DATE, type: t('system.orgTemplate.date') };
|
|
||||||
case 'DATETIME':
|
|
||||||
return { iconName: TemplateIconEnum.DATE, type: t('system.orgTemplate.dateTime') };
|
|
||||||
case 'INT':
|
|
||||||
return { iconName: TemplateIconEnum.NUMBER, type: t('system.orgTemplate.number') };
|
|
||||||
case 'FLOAT':
|
|
||||||
return { iconName: TemplateIconEnum.NUMBER, type: t('system.orgTemplate.number') };
|
|
||||||
case 'MULTIPLE_INPUT':
|
|
||||||
return { iconName: TemplateIconEnum.MULTIPLE_INPUT, type: t('system.orgTemplate.multipleInput') };
|
|
||||||
default:
|
|
||||||
return { type: 'system', iconName: '' };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const moreActions: ActionsItem[] = [
|
const moreActions: ActionsItem[] = [
|
||||||
{
|
{
|
||||||
label: 'system.userGroup.delete',
|
label: 'system.userGroup.delete',
|
||||||
|
@ -214,7 +199,7 @@
|
||||||
try {
|
try {
|
||||||
if (record.id) await deleteOrdField(record.id);
|
if (record.id) await deleteOrdField(record.id);
|
||||||
Message.success(t('system.user.deleteUserSuccess'));
|
Message.success(t('system.user.deleteUserSuccess'));
|
||||||
loadList();
|
fetchData();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
@ -235,28 +220,21 @@
|
||||||
const fieldDrawerRef = ref();
|
const fieldDrawerRef = ref();
|
||||||
const fieldHandler = (type: string, record?: AddOrUpdateField) => {
|
const fieldHandler = (type: string, record?: AddOrUpdateField) => {
|
||||||
showDrawer.value = true;
|
showDrawer.value = true;
|
||||||
if (type === 'edit' && record) fieldDrawerRef.value.isEditHandler(record);
|
if (type === 'edit' && record) fieldDrawerRef.value.editHandler(record);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOk = (record: AddOrUpdateField) => {
|
||||||
|
fieldHandler('edit', record);
|
||||||
};
|
};
|
||||||
|
|
||||||
const successHandler = () => {
|
const successHandler = () => {
|
||||||
loadList();
|
fetchData();
|
||||||
};
|
};
|
||||||
|
|
||||||
const templateList = ref([
|
|
||||||
{
|
|
||||||
value: 'FUNCTIONAL',
|
|
||||||
name: 'system.orgTemplate.caseTemplates',
|
|
||||||
},
|
|
||||||
{ value: 'API', name: 'system.orgTemplate.APITemplates' },
|
|
||||||
{ value: 'UI', name: 'system.orgTemplate.UITemplates' },
|
|
||||||
{ value: 'TEST_PLAN', name: 'system.orgTemplate.testPlanTemplates' },
|
|
||||||
{ value: 'BUG', name: 'system.orgTemplate.defectTemplates' },
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 更新面包屑根据不同的模版
|
// 更新面包屑根据不同的模版
|
||||||
const updateBreadcrumbList = () => {
|
const updateBreadcrumbList = () => {
|
||||||
const { breadcrumbList } = appStore;
|
const { breadcrumbList } = appStore;
|
||||||
const breadTitle = templateList.value.find((item) => item.value === route.query.type);
|
const breadTitle = cardList.find((item) => item.key === route.query.type);
|
||||||
if (breadTitle) {
|
if (breadTitle) {
|
||||||
breadcrumbList[0].locale = breadTitle.name;
|
breadcrumbList[0].locale = breadTitle.name;
|
||||||
appStore.setBreadcrumbList(breadcrumbList);
|
appStore.setBreadcrumbList(breadcrumbList);
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
<template>
|
||||||
|
<MsCard
|
||||||
|
:loading="loading"
|
||||||
|
:title="title"
|
||||||
|
:is-edit="isEdit"
|
||||||
|
has-breadcrumb
|
||||||
|
@save="saveHandler"
|
||||||
|
@save-and-continue="saveHandler(true)"
|
||||||
|
>
|
||||||
|
<template #headerRight>
|
||||||
|
<div class="rightBtn">
|
||||||
|
<a-button v-show="isPreview" type="outline" class="text-[var(--color-text-1)]" @click="togglePreview">{{
|
||||||
|
t('system.orgTemplate.templatePreview')
|
||||||
|
}}</a-button>
|
||||||
|
<a-button v-show="!isPreview" type="outline" class="text-[var(--color-text-1)]" @click="togglePreview">{{
|
||||||
|
t('system.orgTemplate.exitPreview')
|
||||||
|
}}</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<!-- 非预览模式 -->
|
||||||
|
<div v-if="isPreview" class="nonPreview">
|
||||||
|
<a-form ref="formRef" :model="templateForm" layout="vertical">
|
||||||
|
<a-form-item :label="t('system.orgTemplate.templateName')" field="name" asterisk-position="end" required>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="templateForm.name"
|
||||||
|
:placeholder="t('system.orgTemplate.templateNamePlaceholder')"
|
||||||
|
:max-length="255"
|
||||||
|
show-word-limit
|
||||||
|
class="max-w-[732px]"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item field="remark" :label="t('system.orgTemplate.description')" asterisk-position="end">
|
||||||
|
<a-textarea
|
||||||
|
v-model="templateForm.remark"
|
||||||
|
:max-length="255"
|
||||||
|
:placeholder="t('system.orgTemplate.resDescription')"
|
||||||
|
:auto-size="{
|
||||||
|
maxRows: 1,
|
||||||
|
}"
|
||||||
|
class="max-w-[732px]"
|
||||||
|
></a-textarea>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<!-- 已有字段表 -->
|
||||||
|
<TemplateManagementTable ref="templateFieldTableRef" :custom-list="tableFiledDetailList" :is-edit="isEdit" />
|
||||||
|
</div>
|
||||||
|
<!-- 预览模式 -->
|
||||||
|
<PreviewTemplate v-else :select-field="selectFiledToTem" />
|
||||||
|
</MsCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
/**
|
||||||
|
* @description 系统管理-组织-模版-模版管理-创建&编辑
|
||||||
|
*/
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { FormInstance, Message, ValidatedError } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||||
|
import TemplateManagementTable from './templateManagementTable.vue';
|
||||||
|
import PreviewTemplate from './viewTemplate.vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
createOrganizeTemplateInfo,
|
||||||
|
getOrganizeTemplateInfo,
|
||||||
|
updateOrganizeTemplateInfo,
|
||||||
|
} from '@/api/modules/setting/template';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useLeaveUnSaveTip from '@/hooks/useLeaveUnSaveTip';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
|
import useTemplateStore from '@/store/modules/setting/template';
|
||||||
|
import { sleep } from '@/utils';
|
||||||
|
import { scrollIntoView } from '@/utils/dom';
|
||||||
|
|
||||||
|
import type { ActionTemplateManage, CustomField } from '@/models/setting/template';
|
||||||
|
import { SettingRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const templateStore = useTemplateStore();
|
||||||
|
useLeaveUnSaveTip();
|
||||||
|
|
||||||
|
const title = ref('');
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const initTemplateForm = {
|
||||||
|
name: '',
|
||||||
|
remark: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const templateForm = ref({ ...initTemplateForm });
|
||||||
|
|
||||||
|
const tableFiledDetailList = ref([]);
|
||||||
|
// 获取模板详情
|
||||||
|
const getTemplateInfo = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
const res = await getOrganizeTemplateInfo(route.query.id as string);
|
||||||
|
const { name, remark, customFields, scoped, enableThirdPart, scene } = res;
|
||||||
|
templateForm.value.name = name;
|
||||||
|
templateForm.value.remark = remark;
|
||||||
|
// 处理 字段列表
|
||||||
|
tableFiledDetailList.value = customFields;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isEdit = ref(false);
|
||||||
|
const templateFieldTableRef = ref();
|
||||||
|
watchEffect(() => {
|
||||||
|
if (route.query.id) {
|
||||||
|
title.value = t('menu.settings.organization.templateManagementEdit');
|
||||||
|
isEdit.value = true;
|
||||||
|
getTemplateInfo();
|
||||||
|
} else {
|
||||||
|
title.value = t('menu.settings.organization.templateManagementDetail');
|
||||||
|
isEdit.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
// 获取模板参数
|
||||||
|
function getTemplateParams(): ActionTemplateManage {
|
||||||
|
const result: CustomField[] = templateFieldTableRef.value.getCustomFields();
|
||||||
|
const { name, remark } = templateForm.value;
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
remark,
|
||||||
|
customFields: result,
|
||||||
|
scopeId: appStore.currentOrgId,
|
||||||
|
scene: route.query.type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetForm() {
|
||||||
|
templateForm.value = { ...initTemplateForm };
|
||||||
|
}
|
||||||
|
|
||||||
|
const isContinueFlag = ref(false);
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
const params = getTemplateParams();
|
||||||
|
if (isEdit.value) {
|
||||||
|
await updateOrganizeTemplateInfo(params);
|
||||||
|
Message.success(t('system.resourcePool.updateSuccess'));
|
||||||
|
} else {
|
||||||
|
await createOrganizeTemplateInfo(params);
|
||||||
|
Message.success(t('system.orgTemplate.addSuccess'));
|
||||||
|
}
|
||||||
|
if (isContinueFlag.value) {
|
||||||
|
resetForm();
|
||||||
|
} else {
|
||||||
|
await sleep(300);
|
||||||
|
router.push({ name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
async function saveHandler(isContinue = false) {
|
||||||
|
isContinueFlag.value = isContinue;
|
||||||
|
formRef.value?.validate(async (errors: Record<string, ValidatedError> | undefined) => {
|
||||||
|
if (!errors) {
|
||||||
|
save();
|
||||||
|
} else {
|
||||||
|
return scrollIntoView(document.querySelector('.arco-form-item-message'), { block: 'center' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectFiledToTem = ref([]); // 非预览模式模板已选择字段
|
||||||
|
// 是否预览模式
|
||||||
|
const isPreview = ref<boolean>(true); // 默认非预览模式
|
||||||
|
function togglePreview() {
|
||||||
|
isPreview.value = !isPreview.value;
|
||||||
|
if (!isPreview.value) {
|
||||||
|
selectFiledToTem.value = templateFieldTableRef.value.getSelectFiled();
|
||||||
|
templateStore.setPreviewHandler(selectFiledToTem.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
templateFieldTableRef.value.setDefaultField();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.rightBtn {
|
||||||
|
:deep(.arco-btn-outline) {
|
||||||
|
border-color: var(--color-text-input-border) !important;
|
||||||
|
color: var(--color-text-1) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="innerWrapper">
|
<div class="innerWrapper">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="logo-img h-[48px] w-[48px]">
|
<div class="logo-img h-[48px] w-[48px]">
|
||||||
<svg-icon width="36px" height="36px" :name="svgList[props.cardItem.value]"></svg-icon>
|
<svg-icon width="36px" height="36px" :name="props.cardItem.value"></svg-icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="template-operation">
|
<div class="template-operation">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
|
@ -16,12 +16,14 @@
|
||||||
<a-divider direction="vertical" />
|
<a-divider direction="vertical" />
|
||||||
</span>
|
</span>
|
||||||
<span class="operation hover:text-[rgb(var(--primary-5))]">
|
<span class="operation hover:text-[rgb(var(--primary-5))]">
|
||||||
<span>{{ t('system.orgTemplate.TemplateManagement') }}</span> <a-divider direction="vertical" />
|
<span @click="templateManagement">{{ t('system.orgTemplate.TemplateManagement') }}</span>
|
||||||
|
<a-divider v-if="props.cardItem.key == 'BUG'" direction="vertical" />
|
||||||
</span>
|
</span>
|
||||||
<span v-if="props.cardItem.value === 'BUG'" class="operation hover:text-[rgb(var(--primary-5))]">
|
<span v-if="props.cardItem.key === 'BUG'" class="operation hover:text-[rgb(var(--primary-5))]">
|
||||||
<span>{{ t('system.orgTemplate.workflowSetup') }}</span> <a-divider direction="vertical" />
|
<span>{{ t('system.orgTemplate.workflowSetup') }}</span>
|
||||||
|
<a-divider v-if="!props.cardItem.enable" direction="vertical" />
|
||||||
</span>
|
</span>
|
||||||
<span class="rounded p-[2px] hover:bg-[rgb(var(--primary-9))]">
|
<span v-if="!props.cardItem.enable" class="rounded p-[2px] hover:bg-[rgb(var(--primary-9))]">
|
||||||
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect"
|
<MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect"
|
||||||
/></span>
|
/></span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,14 +60,6 @@
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const svgList = ref<Record<string, any>>({
|
|
||||||
FUNCTIONAL: 'caseTemplate',
|
|
||||||
API: 'api_ui_Template',
|
|
||||||
UI: 'api_ui_Template',
|
|
||||||
TEST_PLAN: 'testPlanTemplate',
|
|
||||||
BUG: 'defectTemplate',
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleMoreActionSelect = (item: ActionsItem) => {};
|
const handleMoreActionSelect = (item: ActionsItem) => {};
|
||||||
|
|
||||||
// 字段设置
|
// 字段设置
|
||||||
|
@ -73,7 +67,16 @@
|
||||||
router.push({
|
router.push({
|
||||||
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_FILED_SETTING,
|
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_FILED_SETTING,
|
||||||
query: {
|
query: {
|
||||||
type: props.cardItem.value,
|
type: props.cardItem.key,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const templateManagement = () => {
|
||||||
|
router.push({
|
||||||
|
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT,
|
||||||
|
query: {
|
||||||
|
type: props.cardItem.key,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -0,0 +1,210 @@
|
||||||
|
<template>
|
||||||
|
<MsCard has-breadcrumb simple>
|
||||||
|
<a-alert v-if="isEnable" class="mb-6" type="warning">{{ t('system.orgTemplate.enableTemplateTip') }}</a-alert>
|
||||||
|
<div class="mb-4 flex items-center justify-between">
|
||||||
|
<span v-if="isEnable" class="font-medium">{{ t('system.orgTemplate.templateList') }}</span>
|
||||||
|
<a-button v-else type="primary" :disabled="false" @click="createTemplate">
|
||||||
|
{{ t('system.orgTemplate.createTemplate') }}
|
||||||
|
</a-button>
|
||||||
|
<a-input-search
|
||||||
|
v-model:model-value="keyword"
|
||||||
|
:placeholder="t('system.orgTemplate.searchTip')"
|
||||||
|
class="w-[230px]"
|
||||||
|
allow-clear
|
||||||
|
@search="searchFiled"
|
||||||
|
@press-enter="searchFiled"
|
||||||
|
></a-input-search>
|
||||||
|
</div>
|
||||||
|
<MsBaseTable v-bind="propsRes" ref="tableRef" v-on="propsEvent">
|
||||||
|
<template #name="{ record }">
|
||||||
|
<span class="ml-2">{{ record.name }}</span>
|
||||||
|
<span v-if="record.internal" class="system-flag">{{ t('system.orgTemplate.isSystem') }}</span>
|
||||||
|
</template>
|
||||||
|
<template #operation="{ record }">
|
||||||
|
<div class="flex flex-row flex-nowrap">
|
||||||
|
<MsButton @click="editTemplate(record.id)">{{ t('system.orgTemplate.edit') }}</MsButton>
|
||||||
|
<MsButton class="!mr-0">{{ t('system.orgTemplate.copy') }}</MsButton>
|
||||||
|
<a-divider v-if="!record.internal" direction="vertical" />
|
||||||
|
<MsTableMoreAction
|
||||||
|
v-if="!record.internal"
|
||||||
|
:list="moreActions"
|
||||||
|
@select="(item) => handleMoreActionSelect(item, record)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MsBaseTable>
|
||||||
|
</MsCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
/**
|
||||||
|
* @description 系统管理-组织-模版-模版管理列表
|
||||||
|
*/
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||||
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
|
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
|
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||||
|
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
|
|
||||||
|
import { deleteOrdField, getOrganizeTemplateList } from '@/api/modules/setting/template';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useModal from '@/hooks/useModal';
|
||||||
|
import router from '@/router';
|
||||||
|
import { useAppStore, useTableStore } from '@/store';
|
||||||
|
import useTemplateStore from '@/store/modules/setting/template';
|
||||||
|
import { characterLimit } from '@/utils';
|
||||||
|
|
||||||
|
import type { OrdTemplateManagement } from '@/models/setting/template';
|
||||||
|
import { SettingRouteEnum } from '@/enums/routeEnum';
|
||||||
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const tableStore = useTableStore();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const templateStore = useTemplateStore();
|
||||||
|
const { openModal } = useModal();
|
||||||
|
|
||||||
|
const isEnable = ref<boolean>(false);
|
||||||
|
|
||||||
|
const keyword = ref('');
|
||||||
|
const currentOrd = appStore.currentOrgId;
|
||||||
|
|
||||||
|
const fieldColumns: MsTableColumn = [
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.columnTemplateName',
|
||||||
|
slotName: 'name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: 300,
|
||||||
|
showDrag: true,
|
||||||
|
showInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.description',
|
||||||
|
dataIndex: 'remark',
|
||||||
|
showDrag: true,
|
||||||
|
showInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.columnFieldUpdatedTime',
|
||||||
|
dataIndex: 'updateTime',
|
||||||
|
showDrag: true,
|
||||||
|
showInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.operation',
|
||||||
|
slotName: 'operation',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 200,
|
||||||
|
showInTable: true,
|
||||||
|
showDrag: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
tableStore.initColumn(TableKeyEnum.ORGANIZATION_TEMPLATE_MANAGEMENT, fieldColumns, 'drawer');
|
||||||
|
const { propsRes, propsEvent, loadList, setLoadListParams, setProps } = useTable(getOrganizeTemplateList, {
|
||||||
|
tableKey: TableKeyEnum.ORGANIZATION_TEMPLATE_MANAGEMENT,
|
||||||
|
scroll: { x: '1000px' },
|
||||||
|
selectable: false,
|
||||||
|
noDisable: true,
|
||||||
|
size: 'default',
|
||||||
|
showSetting: true,
|
||||||
|
showPagination: false,
|
||||||
|
heightUsed: 380,
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalList = ref<OrdTemplateManagement[]>([]);
|
||||||
|
// 查询字段
|
||||||
|
const searchFiled = async () => {
|
||||||
|
try {
|
||||||
|
totalList.value = await getOrganizeTemplateList({ organizationId: currentOrd, scene: route.query.type });
|
||||||
|
const filterData = totalList.value.filter((item: OrdTemplateManagement) => item.name.includes(keyword.value));
|
||||||
|
setProps({ data: filterData });
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const moreActions: ActionsItem[] = [
|
||||||
|
{
|
||||||
|
label: 'system.userGroup.delete',
|
||||||
|
danger: true,
|
||||||
|
eventTag: 'delete',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
// 删除模板
|
||||||
|
const handlerDelete = (record: any) => {
|
||||||
|
openModal({
|
||||||
|
type: 'error',
|
||||||
|
title: t('system.orgTemplate.deleteTitle', { name: characterLimit(record.name) }),
|
||||||
|
content: t('system.userGroup.beforeDeleteUserGroup'),
|
||||||
|
okText: t('system.userGroup.confirmDelete'),
|
||||||
|
cancelText: t('system.userGroup.cancel'),
|
||||||
|
okButtonProps: {
|
||||||
|
status: 'danger',
|
||||||
|
},
|
||||||
|
onBeforeOk: async () => {
|
||||||
|
try {
|
||||||
|
if (record.id) await deleteOrdField(record.id);
|
||||||
|
Message.success(t('system.user.deleteUserSuccess'));
|
||||||
|
loadList();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hideCancel: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更多操作
|
||||||
|
const handleMoreActionSelect = (item: ActionsItem, record: any) => {
|
||||||
|
if (item.eventTag === 'delete') {
|
||||||
|
handlerDelete(record);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
const scene = route.query.type;
|
||||||
|
setLoadListParams({ organizationId: currentOrd, scene });
|
||||||
|
await loadList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建模板
|
||||||
|
const createTemplate = () => {
|
||||||
|
templateStore.setPreviewHandler([]);
|
||||||
|
router.push({
|
||||||
|
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL,
|
||||||
|
query: {
|
||||||
|
type: route.query.type,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 编辑模板
|
||||||
|
const editTemplate = (id: string) => {
|
||||||
|
router.push({
|
||||||
|
name: SettingRouteEnum.SETTING_ORGANIZATION_TEMPLATE_MANAGEMENT_DETAIL,
|
||||||
|
query: {
|
||||||
|
type: route.query.type,
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.system-flag {
|
||||||
|
background: var(--color-text-n8);
|
||||||
|
@apply ml-2 rounded p-1 text-xs;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,290 @@
|
||||||
|
<template>
|
||||||
|
<MsBaseTable v-bind="propsRes" ref="tableRef" v-on="propsEvent">
|
||||||
|
<template #name="{ record }">
|
||||||
|
<MsIcon v-if="!record.internal" :type="getIconType(record.type)?.iconName || ''" size="16" />
|
||||||
|
<span class="ml-2">{{ record.name }}</span>
|
||||||
|
<span v-if="record.internal" class="system-flag">{{ t('system.orgTemplate.isSystem') }}</span>
|
||||||
|
</template>
|
||||||
|
<template #defaultValue="{ record }">
|
||||||
|
<div class="form-create-wrapper w-full">
|
||||||
|
<MsFormCreate v-model:api="record.fApi" :rule="record.formRules" :option="configOptions" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #required="{ record }">
|
||||||
|
<a-checkbox v-model="record.required"></a-checkbox>
|
||||||
|
</template>
|
||||||
|
<template #operation="{ record }">
|
||||||
|
<div class="flex flex-row flex-nowrap">
|
||||||
|
<MsButton class="!mr-0" @click="editField(record)">{{ t('system.orgTemplate.edit') }}</MsButton>
|
||||||
|
<a-divider v-if="!record.internal" direction="vertical" />
|
||||||
|
<MsButton v-if="!record.internal" class="!mr-0" @click="deleteSelectedField(record)">{{
|
||||||
|
t('system.orgTemplate.delete')
|
||||||
|
}}</MsButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MsBaseTable>
|
||||||
|
<!-- 添加字段到模版抽屉 -->
|
||||||
|
<AddFieldToTemplateDrawer
|
||||||
|
ref="fieldSelectRef"
|
||||||
|
v-model:visible="showDrawer"
|
||||||
|
:system-data="(systemData as DefinedFieldItem[])"
|
||||||
|
:custom-data="(customData as DefinedFieldItem[])"
|
||||||
|
:selected-data="(templateStore.previewList as DefinedFieldItem[])"
|
||||||
|
@confirm="confirmHandler"
|
||||||
|
@update="updateFieldHandler"
|
||||||
|
/>
|
||||||
|
<a-button class="mt-3 px-0" type="text" @click="addField">
|
||||||
|
<template #icon>
|
||||||
|
<icon-plus class="text-[14px]" />
|
||||||
|
</template>
|
||||||
|
{{ t('system.orgTemplate.createField') }}
|
||||||
|
</a-button>
|
||||||
|
<EditFieldDrawer ref="fieldDrawerRef" v-model:visible="showFieldDrawer" @success="updateFieldHandler" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
import { FieldTypeFormRules } from '@/components/pure/ms-form-create/form-create';
|
||||||
|
import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
|
||||||
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
|
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
|
import AddFieldToTemplateDrawer from './addFieldToTemplateDrawer.vue';
|
||||||
|
import EditFieldDrawer from './editFieldDrawer.vue';
|
||||||
|
|
||||||
|
import { getFieldList } from '@/api/modules/setting/template';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import { useAppStore, useTableStore } from '@/store';
|
||||||
|
import useTemplateStore from '@/store/modules/setting/template';
|
||||||
|
|
||||||
|
import type { CustomField, DefinedFieldItem } from '@/models/setting/template';
|
||||||
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
|
||||||
|
import { getIconType } from './fieldSetting';
|
||||||
|
|
||||||
|
const tableStore = useTableStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const templateStore = useTemplateStore();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
customList: CustomField[]; // 用于回显表格当前选择字段
|
||||||
|
isEdit: boolean; // 是否编辑
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const templateFieldColumns: MsTableColumn = [
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.name',
|
||||||
|
slotName: 'name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: 300,
|
||||||
|
showDrag: true,
|
||||||
|
showInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.defaultValue',
|
||||||
|
dataIndex: 'defaultValue',
|
||||||
|
slotName: 'defaultValue',
|
||||||
|
showDrag: true,
|
||||||
|
showInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.required',
|
||||||
|
dataIndex: 'required',
|
||||||
|
slotName: 'required',
|
||||||
|
width: 180,
|
||||||
|
showDrag: true,
|
||||||
|
showInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.description',
|
||||||
|
dataIndex: 'remark',
|
||||||
|
showDrag: true,
|
||||||
|
showInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.operation',
|
||||||
|
slotName: 'operation',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 200,
|
||||||
|
showInTable: true,
|
||||||
|
showDrag: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
tableStore.initColumn(TableKeyEnum.ORGANIZATION_TEMPLATE_MANAGEMENT_FIELD, templateFieldColumns, 'drawer');
|
||||||
|
const { propsRes, propsEvent, setProps } = useTable(undefined, {
|
||||||
|
tableKey: TableKeyEnum.ORGANIZATION_TEMPLATE_MANAGEMENT_FIELD,
|
||||||
|
scroll: { x: '1000px' },
|
||||||
|
selectable: false,
|
||||||
|
noDisable: true,
|
||||||
|
size: 'default',
|
||||||
|
showSetting: true,
|
||||||
|
showPagination: false,
|
||||||
|
heightUsed: 560,
|
||||||
|
enableDrag: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const configOptions = ref({
|
||||||
|
resetBtn: false,
|
||||||
|
submitBtn: false,
|
||||||
|
on: false,
|
||||||
|
form: {
|
||||||
|
layout: 'vertical',
|
||||||
|
labelAlign: 'left',
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
gutter: 0,
|
||||||
|
},
|
||||||
|
wrap: {
|
||||||
|
'asterisk-position': 'end',
|
||||||
|
'validate-trigger': ['change'],
|
||||||
|
'hide-label': true,
|
||||||
|
'hide-asterisk': true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const showDrawer = ref<boolean>(false);
|
||||||
|
|
||||||
|
const data = ref<DefinedFieldItem[]>([]);
|
||||||
|
const systemData = ref<DefinedFieldItem[]>([]);
|
||||||
|
const customData = ref<DefinedFieldItem[]>([]);
|
||||||
|
const totalTemplateField = ref<DefinedFieldItem[]>([]);
|
||||||
|
const currentOrd = appStore.currentOrgId;
|
||||||
|
|
||||||
|
// 处理表单数据格式
|
||||||
|
const getFieldOptionList = () => {
|
||||||
|
totalTemplateField.value = totalTemplateField.value.map((item: any) => {
|
||||||
|
const currentFormRules = FieldTypeFormRules[item.type];
|
||||||
|
let selectOptions: any = [];
|
||||||
|
if (item.options && item.options.length) {
|
||||||
|
selectOptions = item.options.map((optionItem: any) => {
|
||||||
|
return {
|
||||||
|
label: optionItem.text,
|
||||||
|
value: optionItem.value,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
currentFormRules.options = selectOptions;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
formRules: [{ ...currentFormRules, value: item.value, props: { options: selectOptions } }],
|
||||||
|
fApi: null,
|
||||||
|
required: item.internal,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取当前用例模版下所有已存在字段
|
||||||
|
const getClassifyField = async () => {
|
||||||
|
try {
|
||||||
|
totalTemplateField.value = await getFieldList({ organizationId: currentOrd, scene: route.query.type });
|
||||||
|
getFieldOptionList();
|
||||||
|
systemData.value = totalTemplateField.value.filter((item: any) => item.internal);
|
||||||
|
customData.value = totalTemplateField.value.filter((item: any) => !item.internal);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确定处理字段表单数据
|
||||||
|
const confirmHandler = (dataList: DefinedFieldItem[]) => {
|
||||||
|
data.value = dataList;
|
||||||
|
setProps({ data: data.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const showFieldDrawer = ref<boolean>(false);
|
||||||
|
const fieldDrawerRef = ref();
|
||||||
|
|
||||||
|
// 编辑字段
|
||||||
|
const editField = (record: DefinedFieldItem) => {
|
||||||
|
showFieldDrawer.value = true;
|
||||||
|
fieldDrawerRef.value.editHandler(record);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fieldSelectRef = ref();
|
||||||
|
// 添加字段
|
||||||
|
const addField = async () => {
|
||||||
|
showDrawer.value = true;
|
||||||
|
await getClassifyField();
|
||||||
|
fieldSelectRef.value.showSelectField();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 编辑更新已选择字段
|
||||||
|
const updateFieldHandler = async () => {
|
||||||
|
await getClassifyField();
|
||||||
|
data.value =
|
||||||
|
totalTemplateField.value.filter((item) => data.value.some((dataItem) => item.id === dataItem.id)) || [];
|
||||||
|
if (data.value.length > 0) {
|
||||||
|
setProps({ data: data.value });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除已选择字段
|
||||||
|
const deleteSelectedField = (record: DefinedFieldItem) => {
|
||||||
|
data.value = data.value.filter((item) => item.id !== record.id);
|
||||||
|
setProps({ data: data.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取table内字段customFields
|
||||||
|
const getCustomFields = () => {
|
||||||
|
return data.value.map((item) => {
|
||||||
|
return {
|
||||||
|
fieldId: item.id,
|
||||||
|
fieldName: item.name,
|
||||||
|
required: item.required,
|
||||||
|
apiFieldId: '',
|
||||||
|
defaultValue: item.fApi.formData().fieldName,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// 外界获取表格字段进行存储
|
||||||
|
const getSelectFiled = () => {
|
||||||
|
return data.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => data.value,
|
||||||
|
(val) => {
|
||||||
|
templateStore.setPreviewHandler(val as DefinedFieldItem[]);
|
||||||
|
setProps({ data: templateStore.previewList });
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const setDefaultField = async () => {
|
||||||
|
await getClassifyField();
|
||||||
|
data.value = systemData.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const showDetailFields = () => {};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.isEdit) {
|
||||||
|
showDetailFields();
|
||||||
|
} else {
|
||||||
|
data.value = templateStore.previewList;
|
||||||
|
setProps({ data: templateStore.previewList });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
getCustomFields,
|
||||||
|
getSelectFiled,
|
||||||
|
setDefaultField,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.form-create-wrapper {
|
||||||
|
:deep(.arco-form-item) {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
:deep(.arco-picker) {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,250 @@
|
||||||
|
<template>
|
||||||
|
<div class="wrapper-preview">
|
||||||
|
<div class="preview-left pr-4">
|
||||||
|
<a-form ref="viewFormRef" class="rounded-[4px]" :model="viewForm" layout="vertical">
|
||||||
|
<a-form-item
|
||||||
|
field="caseName"
|
||||||
|
:label="t('system.orgTemplate.caseName')"
|
||||||
|
:rules="[{ required: true, message: t('system.orgTemplate.caseNamePlaceholder') }]"
|
||||||
|
required
|
||||||
|
asterisk-position="end"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model="viewForm.name"
|
||||||
|
:max-length="255"
|
||||||
|
:placeholder="t('system.orgTemplate.caseNamePlaceholder')"
|
||||||
|
show-word-limit
|
||||||
|
allow-clear
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item field="precondition" :label="t('system.orgTemplate.precondition')" asterisk-position="end">
|
||||||
|
<MsRichText v-model="viewForm.precondition" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item field="step" :label="t('system.orgTemplate.stepDescription')" class="relative">
|
||||||
|
<div class="absolute left-16 top-0">
|
||||||
|
<a-divider direction="vertical" />
|
||||||
|
<a-dropdown :popup-max-height="false" @select="handleSelectType">
|
||||||
|
<span class="text-[14px] text-[var(--color-text-4)]"
|
||||||
|
>{{ t('system.orgTemplate.changeType') }} <icon-down
|
||||||
|
/></span>
|
||||||
|
<template #content>
|
||||||
|
<a-doption> {{ t('system.orgTemplate.stepDescription') }}</a-doption>
|
||||||
|
<a-doption>{{ t('system.orgTemplate.textDescription') }}</a-doption>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</div>
|
||||||
|
<!-- 步骤描述 -->
|
||||||
|
<div class="w-full">
|
||||||
|
<MsBaseTable v-bind="propsRes" ref="stepTableRef" v-on="propsEvent">
|
||||||
|
<template #index="{ rowIndex }">
|
||||||
|
{{ rowIndex + 1 }}
|
||||||
|
</template>
|
||||||
|
<template #caseStep="{ record }">
|
||||||
|
<a-input v-if="record.showStep" v-model="record.caseStep" class="w-max-[267px]" />
|
||||||
|
<span v-else-if="record.caseStep && !record.showStep">{{ record.caseStep }}</span>
|
||||||
|
<span
|
||||||
|
v-else-if="!record.caseStep && !record.showStep"
|
||||||
|
class="placeholder text-[var(--color-text-brand)]"
|
||||||
|
>{{ t('system.orgTemplate.stepTip') }}</span
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #expectedResult="{ record }">
|
||||||
|
<a-input v-if="record.showExpected" v-model="record.expectedResult" class="w-max-[267px]" />
|
||||||
|
<span v-else-if="record.expectedResult && !record.showExpected">{{ record.caseStep }}</span>
|
||||||
|
<span
|
||||||
|
v-else-if="!record.expectedResult && !record.showExpected"
|
||||||
|
class="placeholder text-[var(--color-text-brand)]"
|
||||||
|
>{{ t('system.orgTemplate.expectationTip') }}</span
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #operation="{ record }">
|
||||||
|
<MsTableMoreAction
|
||||||
|
v-if="!record.internal"
|
||||||
|
:list="moreActions"
|
||||||
|
@select="(item) => handleMoreActionSelect(item)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</MsBaseTable>
|
||||||
|
</div>
|
||||||
|
<a-button class="mt-2 px-0" type="text" @click="addStep">
|
||||||
|
<template #icon>
|
||||||
|
<icon-plus class="text-[14px]" />
|
||||||
|
</template>
|
||||||
|
{{ t('system.orgTemplate.addStep') }}
|
||||||
|
</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item field="remark" label="备注"> <MsRichText v-model="viewForm.remark" /> </a-form-item>
|
||||||
|
<a-form-item field="attachment" label="添加附件">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="mb-1"
|
||||||
|
><a-button type="outline">
|
||||||
|
<template #icon> <icon-plus class="text-[14px]" /> </template
|
||||||
|
>{{ t('system.orgTemplate.addAttachment') }}</a-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="text-[var(--color-text-4)]">{{ t('system.orgTemplate.addAttachmentTip') }}</div>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
<div class="preview-right px-4">
|
||||||
|
<MsFormCreate :form-rule="formRules" :form-create-key="FormCreateKeyEnum.ORGANIZE_TEMPLATE_PREVIEW_TEMPLATE" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import MsFormCreate from '@/components/pure/ms-form-create/form-create.vue';
|
||||||
|
import type { FormItem } from '@/components/pure/ms-form-create/types';
|
||||||
|
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||||
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
|
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
|
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||||
|
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
|
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import { useTableStore } from '@/store';
|
||||||
|
|
||||||
|
import type { DefinedFieldItem } from '@/models/setting/template';
|
||||||
|
import { FormCreateKeyEnum } from '@/enums/formCreateEnum';
|
||||||
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const tableStore = useTableStore();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
selectField: DefinedFieldItem[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const templateFieldColumns: MsTableColumn = [
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.numberIndex',
|
||||||
|
dataIndex: 'index',
|
||||||
|
slotName: 'index',
|
||||||
|
width: 100,
|
||||||
|
showDrag: false,
|
||||||
|
showInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.useCaseStep',
|
||||||
|
slotName: 'caseStep',
|
||||||
|
dataIndex: 'caseStep',
|
||||||
|
showDrag: true,
|
||||||
|
showInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.expectedResult',
|
||||||
|
dataIndex: 'expectedResult',
|
||||||
|
slotName: 'expectedResult',
|
||||||
|
showDrag: true,
|
||||||
|
showInTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'system.orgTemplate.operation',
|
||||||
|
slotName: 'operation',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 200,
|
||||||
|
showInTable: true,
|
||||||
|
showDrag: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
tableStore.initColumn(TableKeyEnum.ORGANIZATION_TEMPLATE_MANAGEMENT_STEP, templateFieldColumns, 'drawer');
|
||||||
|
const { propsRes, propsEvent, setProps } = useTable(undefined, {
|
||||||
|
tableKey: TableKeyEnum.ORGANIZATION_TEMPLATE_MANAGEMENT_STEP,
|
||||||
|
scroll: { x: '800px' },
|
||||||
|
selectable: false,
|
||||||
|
noDisable: true,
|
||||||
|
size: 'default',
|
||||||
|
showSetting: true,
|
||||||
|
showPagination: false,
|
||||||
|
enableDrag: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const viewForm = ref({
|
||||||
|
name: '',
|
||||||
|
precondition: '',
|
||||||
|
value: '',
|
||||||
|
remark: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSelectType = () => {};
|
||||||
|
|
||||||
|
const stepTableRef = ref();
|
||||||
|
|
||||||
|
const moreActions: ActionsItem[] = [
|
||||||
|
{
|
||||||
|
label: 'system.orgTemplate.copy',
|
||||||
|
danger: true,
|
||||||
|
eventTag: 'copy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'system.orgTemplate.delete',
|
||||||
|
danger: true,
|
||||||
|
eventTag: 'delete',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handlerDelete = () => {};
|
||||||
|
// 更多操作
|
||||||
|
const handleMoreActionSelect = (item: ActionsItem) => {
|
||||||
|
if (item.eventTag === 'delete') {
|
||||||
|
handlerDelete();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addStep = () => {};
|
||||||
|
|
||||||
|
const formRuleField = ref<FormItem[][]>([]);
|
||||||
|
const formRules = ref<FormItem[]>([]);
|
||||||
|
// 处理表单格式
|
||||||
|
const getFormRules = () => {
|
||||||
|
if (props.selectField && props.selectField.length) {
|
||||||
|
props.selectField.forEach((item: DefinedFieldItem) => {
|
||||||
|
const currentFormItem = item.formRules?.map((rule: any) => {
|
||||||
|
const optionsItem = rule.options.map((opt: any) => {
|
||||||
|
return {
|
||||||
|
text: opt.label,
|
||||||
|
value: opt.value,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
type: item.type,
|
||||||
|
name: rule.field,
|
||||||
|
label: item.name,
|
||||||
|
value: rule.value,
|
||||||
|
options: optionsItem,
|
||||||
|
required: item.required,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
formRuleField.value.push(currentFormItem as FormItem[]);
|
||||||
|
});
|
||||||
|
const result = formRuleField.value.flatMap((item) => item);
|
||||||
|
formRules.value = result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setProps({ data: [{ id: 1, showStep: false, showExpected: false }] });
|
||||||
|
getFormRules();
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
getFormRules,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.wrapper-preview {
|
||||||
|
display: flex;
|
||||||
|
.preview-left {
|
||||||
|
width: 100%;
|
||||||
|
border-right: 1px solid var(--color-text-n8);
|
||||||
|
}
|
||||||
|
.preview-right {
|
||||||
|
width: 428px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -35,25 +35,18 @@
|
||||||
|
|
||||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||||
import MsCardList from '@/components/business/ms-card-list/index.vue';
|
import MsCardList from '@/components/business/ms-card-list/index.vue';
|
||||||
import TemplateItem from '@/components/business/ms-template-card/index.vue';
|
import TemplateItem from './components/templateItem.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useVisit from '@/hooks/useVisit';
|
import useVisit from '@/hooks/useVisit';
|
||||||
|
import useTemplateStore from '@/store/modules/setting/template';
|
||||||
|
|
||||||
|
import { cardList } from './components/fieldSetting';
|
||||||
|
|
||||||
|
const templateStore = useTemplateStore();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const cardList = ref([
|
|
||||||
{
|
|
||||||
id: 1001,
|
|
||||||
value: 'FUNCTIONAL',
|
|
||||||
name: t('system.orgTemplate.caseTemplates'),
|
|
||||||
},
|
|
||||||
{ id: 1002, value: 'API', name: t('system.orgTemplate.APITemplates') },
|
|
||||||
{ id: 1003, value: 'UI', name: t('system.orgTemplate.UITemplates') },
|
|
||||||
{ id: 1004, value: 'TEST_PLAN', name: t('system.orgTemplate.testPlanTemplates') },
|
|
||||||
{ id: 1005, value: 'BUG', name: t('system.orgTemplate.defectTemplates') },
|
|
||||||
]);
|
|
||||||
|
|
||||||
const visitedKey = 'notRemind';
|
const visitedKey = 'notRemind';
|
||||||
const { addVisited } = useVisit(visitedKey);
|
const { addVisited } = useVisit(visitedKey);
|
||||||
const { getIsVisited } = useVisit(visitedKey);
|
const { getIsVisited } = useVisit(visitedKey);
|
||||||
|
@ -68,6 +61,9 @@
|
||||||
const doCheckIsTip = () => {
|
const doCheckIsTip = () => {
|
||||||
isShowTip.value = !getIsVisited();
|
isShowTip.value = !getIsVisited();
|
||||||
};
|
};
|
||||||
|
onBeforeMount(() => {
|
||||||
|
templateStore.setStatus();
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
doCheckIsTip();
|
doCheckIsTip();
|
||||||
|
|
|
@ -22,9 +22,11 @@ export default {
|
||||||
'system.orgTemplate.TemplateManagement': 'Management',
|
'system.orgTemplate.TemplateManagement': 'Management',
|
||||||
'system.orgTemplate.workflowSetup': 'Workflow',
|
'system.orgTemplate.workflowSetup': 'Workflow',
|
||||||
'system.orgTemplate.enable': 'Enable',
|
'system.orgTemplate.enable': 'Enable',
|
||||||
'system.orgTemplate.update': 'Update',
|
'system.orgTemplate.update': 'Update Field',
|
||||||
'system.orgTemplate.addField': 'Add Field',
|
'system.orgTemplate.addField': 'Create Field',
|
||||||
|
'system.orgTemplate.createField': 'Add Field',
|
||||||
'system.orgTemplate.enableDescription': 'The project template is enabled. Field Settings are inoperable',
|
'system.orgTemplate.enableDescription': 'The project template is enabled. Field Settings are inoperable',
|
||||||
|
'system.orgTemplate.fieldLimit': 'You can add a maximum of 20 fields',
|
||||||
'system.orgTemplate.isSystem': 'System',
|
'system.orgTemplate.isSystem': 'System',
|
||||||
'system.orgTemplate.fieldList': 'Field list',
|
'system.orgTemplate.fieldList': 'Field list',
|
||||||
'system.orgTemplate.addOptions': 'Add an option',
|
'system.orgTemplate.addOptions': 'Add an option',
|
||||||
|
@ -57,4 +59,37 @@ export default {
|
||||||
'system.orgTemplate.dateFormat': 'Date format',
|
'system.orgTemplate.dateFormat': 'Date format',
|
||||||
'system.orgTemplate.formatPlaceholder': 'Please select a format',
|
'system.orgTemplate.formatPlaceholder': 'Please select a format',
|
||||||
'system.orgTemplate.optionsPlaceholder': 'Please enter options',
|
'system.orgTemplate.optionsPlaceholder': 'Please enter options',
|
||||||
|
'system.orgTemplate.columnTemplateName': 'Template name',
|
||||||
|
'system.orgTemplate.copy': 'Copy',
|
||||||
|
'system.orgTemplate.defaultValue': 'Default value',
|
||||||
|
'system.orgTemplate.required': 'Required',
|
||||||
|
'system.orgTemplate.enableTemplateTip': 'The project template is enabled. The organization template is unavailable',
|
||||||
|
'system.orgTemplate.templateList': 'Template list',
|
||||||
|
'system.orgTemplate.createTemplate': 'Create',
|
||||||
|
'system.orgTemplate.templatePreview': 'Template preview',
|
||||||
|
'system.orgTemplate.templateName': 'Template name',
|
||||||
|
'system.orgTemplate.templateNamePlaceholder': 'Please enter a template name',
|
||||||
|
'system.orgTemplate.optionalField': 'Optional Field',
|
||||||
|
'system.orgTemplate.selectAll': 'select All',
|
||||||
|
'system.orgTemplate.systemField': 'System Field',
|
||||||
|
'system.orgTemplate.customField': 'Custom Field',
|
||||||
|
'system.orgTemplate.selectedField': 'Selected Field',
|
||||||
|
'system.orgTemplate.clear': 'Clear',
|
||||||
|
'system.orgTemplate.addSuccess': 'create successfully',
|
||||||
|
'system.orgTemplate.updateSuccess': 'update successfully',
|
||||||
|
'system.orgTemplate.exitPreview': 'Exit Preview',
|
||||||
|
'system.orgTemplate.useCaseStep': 'useCase Step',
|
||||||
|
'system.orgTemplate.expectedResult': 'Expected Result',
|
||||||
|
'system.orgTemplate.numberIndex': 'Index',
|
||||||
|
'system.orgTemplate.addStep': 'Add Step',
|
||||||
|
'system.orgTemplate.caseName': 'Use case name',
|
||||||
|
'system.orgTemplate.caseNamePlaceholder': 'Please enter a use case name',
|
||||||
|
'system.orgTemplate.precondition': 'precondition',
|
||||||
|
'system.orgTemplate.stepDescription': 'Step description',
|
||||||
|
'system.orgTemplate.changeType': 'Change type',
|
||||||
|
'system.orgTemplate.textDescription': 'Text description',
|
||||||
|
'system.orgTemplate.stepTip': 'Please enter steps',
|
||||||
|
'system.orgTemplate.expectationTip': 'Please enter expectations',
|
||||||
|
'system.orgTemplate.addAttachment': 'Add attachment',
|
||||||
|
'system.orgTemplate.addAttachmentTip': 'Support any type of file, the file size does not exceed 500MB',
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,9 +22,11 @@ export default {
|
||||||
'system.orgTemplate.TemplateManagement': '模版管理',
|
'system.orgTemplate.TemplateManagement': '模版管理',
|
||||||
'system.orgTemplate.workflowSetup': '工作流设置',
|
'system.orgTemplate.workflowSetup': '工作流设置',
|
||||||
'system.orgTemplate.enable': '启用项目模版',
|
'system.orgTemplate.enable': '启用项目模版',
|
||||||
'system.orgTemplate.update': '更新',
|
'system.orgTemplate.update': '更新字段',
|
||||||
'system.orgTemplate.addField': '新增字段',
|
'system.orgTemplate.addField': '新增字段',
|
||||||
|
'system.orgTemplate.createField': '添加字段',
|
||||||
'system.orgTemplate.enableDescription': '已启用项目模版,字段设置不可操作',
|
'system.orgTemplate.enableDescription': '已启用项目模版,字段设置不可操作',
|
||||||
|
'system.orgTemplate.fieldLimit': '最多可新增 20 个字段',
|
||||||
'system.orgTemplate.isSystem': '系统',
|
'system.orgTemplate.isSystem': '系统',
|
||||||
'system.orgTemplate.fieldList': '字段列表',
|
'system.orgTemplate.fieldList': '字段列表',
|
||||||
'system.orgTemplate.addOptions': '添加一个选项',
|
'system.orgTemplate.addOptions': '添加一个选项',
|
||||||
|
@ -56,4 +58,40 @@ export default {
|
||||||
'system.orgTemplate.dateFormat': '日期格式',
|
'system.orgTemplate.dateFormat': '日期格式',
|
||||||
'system.orgTemplate.formatPlaceholder': '请选择格式',
|
'system.orgTemplate.formatPlaceholder': '请选择格式',
|
||||||
'system.orgTemplate.optionsPlaceholder': '请输入选项',
|
'system.orgTemplate.optionsPlaceholder': '请输入选项',
|
||||||
|
'system.orgTemplate.updateTip': '确认更新 `{name}` 吗?',
|
||||||
|
'system.orgTemplate.updateDescription': '更新后,所使用该字段模版将一起修改',
|
||||||
|
'system.orgTemplate.confirm': '确定',
|
||||||
|
'system.orgTemplate.columnTemplateName': '模版名称',
|
||||||
|
'system.orgTemplate.copy': '复制',
|
||||||
|
'system.orgTemplate.defaultValue': '默认值',
|
||||||
|
'system.orgTemplate.required': '是否必填',
|
||||||
|
'system.orgTemplate.enableTemplateTip': '已启用项目模版,组织模版不可操作',
|
||||||
|
'system.orgTemplate.templateList': '模板列表',
|
||||||
|
'system.orgTemplate.createTemplate': '创建模版',
|
||||||
|
'system.orgTemplate.templatePreview': '模板预览',
|
||||||
|
'system.orgTemplate.templateName': '模板名称',
|
||||||
|
'system.orgTemplate.templateNamePlaceholder': '请输入模版名称',
|
||||||
|
'system.orgTemplate.optionalField': '可选字段',
|
||||||
|
'system.orgTemplate.selectAll': '全选',
|
||||||
|
'system.orgTemplate.systemField': '系统字段',
|
||||||
|
'system.orgTemplate.customField': '自定义字段',
|
||||||
|
'system.orgTemplate.selectedField': '已选字段',
|
||||||
|
'system.orgTemplate.clear': '清空',
|
||||||
|
'system.orgTemplate.addSuccess': '新增成功',
|
||||||
|
'system.orgTemplate.updateSuccess': '更新成功',
|
||||||
|
'system.orgTemplate.exitPreview': '退出预览',
|
||||||
|
'system.orgTemplate.useCaseStep': '用例步骤',
|
||||||
|
'system.orgTemplate.expectedResult': '预期结果',
|
||||||
|
'system.orgTemplate.numberIndex': '序号',
|
||||||
|
'system.orgTemplate.addStep': '添加步骤',
|
||||||
|
'system.orgTemplate.caseName': '用例名称',
|
||||||
|
'system.orgTemplate.caseNamePlaceholder': '请输入用例名称',
|
||||||
|
'system.orgTemplate.precondition': '前置条件',
|
||||||
|
'system.orgTemplate.stepDescription': '步骤描述',
|
||||||
|
'system.orgTemplate.changeType': '更改类型',
|
||||||
|
'system.orgTemplate.textDescription': '文本描述',
|
||||||
|
'system.orgTemplate.stepTip': '请输入步骤',
|
||||||
|
'system.orgTemplate.expectationTip': '请输入预期',
|
||||||
|
'system.orgTemplate.addAttachment': '添加附件',
|
||||||
|
'system.orgTemplate.addAttachmentTip': '支持任意类型文件,文件大小不超过 500MB',
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<div class="ms-scroll">
|
<div class="ms-scroll">
|
||||||
<div v-for="(item, index) in recordItem.pluginForms" :key="item.id" class="ms-self">
|
<div v-for="(item, index) in recordItem.pluginForms" :key="item.id" class="ms-self">
|
||||||
<span class="circle text-xs leading-[16px]"> {{ index + 1 }} </span>
|
<span class="circle text-xs leading-[16px]"> {{ index + 1 }} </span>
|
||||||
<span class="cursor-pointer text-[rgb(var(--primary-5))]" @click="emit('MessageEvent', recordItem, item)">{{
|
<span class="cursor-pointer text-[rgb(var(--primary-5))]" @click="getScriptEmit(recordItem, item)">{{
|
||||||
item.name
|
item.name
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,8 +24,16 @@
|
||||||
recordItem: PluginItem;
|
recordItem: PluginItem;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'MessageEvent', record: PluginItem, item: PluginForms): void;
|
(e: 'messageEvent', record: PluginItem, item: PluginForms): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const originPluginId = ref('');
|
||||||
|
const getScriptEmit = (record: PluginItem, item: PluginForms) => {
|
||||||
|
if (originPluginId.value !== item.id) {
|
||||||
|
emit('messageEvent', record, item);
|
||||||
|
originPluginId.value = item.id;
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
|
|
@ -35,6 +35,7 @@ export default {
|
||||||
'system.resourcePool.disablePoolCancel': 'Cancel',
|
'system.resourcePool.disablePoolCancel': 'Cancel',
|
||||||
'system.plugin.deletePluginSuccess': 'Delete successfully',
|
'system.plugin.deletePluginSuccess': 'Delete successfully',
|
||||||
'system.plugin.disablePluginSuccess': 'Disabled successfully',
|
'system.plugin.disablePluginSuccess': 'Disabled successfully',
|
||||||
|
'system.plugin.enablePluginSuccess': 'Enabled successfully',
|
||||||
'system.plugin.disablePluginContent':
|
'system.plugin.disablePluginContent':
|
||||||
'The project can not integrate with the platform and the default template of the platform is not available, be careful!',
|
'The project can not integrate with the platform and the default template of the platform is not available, be careful!',
|
||||||
'system.plugin.alertDescribe':
|
'system.plugin.alertDescribe':
|
||||||
|
|
|
@ -58,6 +58,7 @@ export default {
|
||||||
'system.plugin.currentScene': '当前场景',
|
'system.plugin.currentScene': '当前场景',
|
||||||
'system.plugin.deletePluginSuccess': '删除成功',
|
'system.plugin.deletePluginSuccess': '删除成功',
|
||||||
'system.plugin.disablePluginSuccess': '禁用成功',
|
'system.plugin.disablePluginSuccess': '禁用成功',
|
||||||
|
'system.plugin.enablePluginSuccess': '启用成功',
|
||||||
'system.plugin.disablePluginContent': '项目无法与该平台集成且该平台默认模版不可用,谨慎操作!',
|
'system.plugin.disablePluginContent': '项目无法与该平台集成且该平台默认模版不可用,谨慎操作!',
|
||||||
'system.plugin.alertDescribe':
|
'system.plugin.alertDescribe':
|
||||||
'MeterSphereV2.10LTS 版本支持 DevOps、API 导入、请求、项目管理、协议类型的插件,具体支持插件请',
|
'MeterSphereV2.10LTS 版本支持 DevOps、API 导入、请求、项目管理、协议类型的插件,具体支持插件请',
|
||||||
|
|
Loading…
Reference in New Issue