Appearance
AI 后端编码规范
开发规则
- 鲸舰的功能包,分为一个个文件夹,需要开发的文件处于packages文件夹内,比如: jingjian-platform-deployment/packages/Caches/ 该文件夹就代表一个功能。
- jingjian-platform-deployment/packages/Caches/ 文件夹下包含一个index.ts文件,该文件为了注册组件。
- vue组件内,从上到下的顺序是 template, script, style
- 前端文件开发,需要按照界面模板来进行,以下会专门列出界面模板的内容。
界面模板
列表(查询)界面模板
- 列表界面的文件命名一般以xxxList.vue结尾。
- 列表界面使用结构:
html
<template>
<app-page>
<app-table-layout>
<template #filter>
<app-filter-container ref="filterContainer" v-model="conditions" :loading="loadingQuery" show-more :filters="defineFilters" @search="search">
</app-filter-container>
</template>
<template #operates>
<el-button type="primary" @click="showNew" :icon="Plus">新增</el-button>
</template>
<template #table>
<app-table :loading="loadingQuery" v-model:searchEvent="searchEvent" v-model:selection="selection" @search="search" @rowDblclick="showEdit" :columns="defineColumns" :data="data" show-page :show-export="false"></app-table>
</template>
</app-table-layout>
</app-page>
</template>javascript
<script lang="ts" setup>
const loadingQuery = ref<boolean>(false)
const searchEvent = ref(new SearchEvent())
const data = ref()
const edit = ref()
const menuDialog = ref()
const conditions = ref<any>({})
const selection = ref<any>([])
const searchUrl = '/deployment/ProjectAbilityMenuGroup/list'
const deleteUrl = '/deployment/ProjectAbilityMenuGroup/delete'
onMounted(() => nextTick(() => search()))
// 删除
const onDelete = async (row: any) => {
try {
if (!await UIFactory.confirm('您确定要删除吗')) return
const res: HttpResult<any> = await jingjian.https.post(deleteUrl, { ids: row.id ? [row.id] : selection.value?.map((x: any) => x.id) })
if (!res.success) throw Error(res.msg)
UIFactory.success("删除成功")
search()
}
catch (ex: any) {
UIFactory.error(ex.message)
}
}
// 操作
const showNew = () => edit.value.show()
// 编辑
const showEdit = (entity?: any) => edit.value.show(entity)
const showSetting = (entity?: any) => menuDialog.value.show(entity)
// 加载列表数据
const search = async () => {
try
{
loadingQuery.value = true
let request = {
...conditions.value,
sortField: searchEvent.value.sortField,
sortType: searchEvent.value.sortType,
page: searchEvent.value.pageContent.currentPage,
limit: searchEvent.value.pageContent.pageSize,
projectId: 'a569cf35-aa9b-4d33-9968-505fc7608445'
}
const res: HttpResult<any> = await jingjian.https.get(searchUrl, request);
if (!res.success) throw new Error(res.msg);
data.value = res.data;
searchEvent.value.pageContent.totalRecords = res.totals;
}
catch (error: any)
{
UIFactory.error(error.message);
}
finally
{
loadingQuery.value = false;
}
}
// 定义条件
const defineFilters: Array<AppFilter> = [
{
type: EnumAppFilterType.string,
label: "分组名称",
key: "name",
placeholder: "请输入分组名称",
options: {
value: '',
}
},
{
type: EnumAppFilterType.select,
label: "是否启用",
key: "isEnabled",
placeholder: "请选择是否启用",
options: {
appSelectType: AppSelectType.boolean,
falseLabel: '禁用',
trueLabel: '启用',
}
}
]
// 定义表格
const defineColumns = [
{
prop: "createTime",
label: "创建时间",
width: 180,
align: 'center',
template: {
type: AppTableTemplateType.datetime,
format: 'YYYY-MM-DD HH:mm:ss'
}
},
{
prop: "name",
label: "分组名称",
width: 300,
align: 'left',
showOverflowTooltip: true,
},
{
label: "后台菜单",
width: 200,
align: 'center',
render: (scope: any) => h('a', {
style: 'color: blue; cursor: pointer; color: #409EFF;',
onClick: () => menuDialog.value.show(scope.row, EnumMenuType.后台)
}, '[设置]')
},
{
label: "收银台菜单",
width: 200,
align: 'center',
render: (scope: any) => h('a', {
style: 'color: blue; cursor: pointer; color: #409EFF;',
onClick: () => menuDialog.value.show(scope.row, EnumMenuType.收银台)
}, '[设置]')
},
{
label: "鲸舰助手菜单",
width: 200,
align: 'center',
render: (scope: any) => h('a', {
style: 'color: blue; cursor: pointer; color: #409EFF;',
onClick: () => menuDialog.value.show(scope.row, EnumMenuType.鲸舰助手)
}, '[设置]')
},
{
label: "#"
},
{
prop: "isEnabled",
label: "是否启用",
width: 100,
align: 'center',
template: {
type: AppTableTemplateType.boolean,
trueLabel: '启用',
falseLabel: '禁用'
}
},
{
label: "操作",
fixed: "right",
width: 130,
align: 'center',
render: (scope: any) =>
h("div", { style: "display: flex" }, [
h(
ElButton,
{
type: "primary",
size: "small",
onClick: () => showEdit(scope.row),
},
{ default: () => "编辑" }
),
h(
ElButton,
{
type: "danger",
size: "small",
onClick: () => onDelete(scope.row),
},
{ default: () => "删除" }
),
]),
},
]
</script>- 其中loading代表查询时的状态,应用在table和组件中。如果界面产生多个loading状态时,则命名规则为“loading+操作名称”,比如:loadingSearch。
- 条件定义defineFilters 和 表格的列属性定义 defineColumns 放在script的最下面。
- 查询方法的命名默认为search。
- 如果该列表界面可编辑,则需要增加编辑界面,编辑界面,需参考“编辑界面模板”。
- 如果界面增加操作按钮,比如编辑/删除按钮,则增加在 defineColumns 中,且该列的宽度,需要根据按钮的多少,来进行调整,不要产生换行。
- 查询条件的可用模板,可以参考:http://hela.jingjianx.vip/uicomponents/elementplus/app-filter-container.html
- 表格列属性的可用模板,可以参考:http://hela.jingjianx.vip/uicomponents/elementplus/app-table.html
- 与服务器通信使用到的URL,定义在属性下方,在方法中使用时,仅使用变量即可。
编辑界面模板
- 编辑界面的文件命名一般以xxxEdit.vue结尾。
- 编辑界面使用两个文件: xxxEdit.vue, xxxForm.ts。
- 编辑界面使用结构:
html
<!-- template 界面 -->
<template>
<app-edit-container ref="container" title="新增/修改Redis" @close="close" draggable :size="600" :offset="60">
<el-form ref="formRef" :model="form" label-width="140px" :rules="rules">
<el-form-item label="Redis名称" prop="redisName">
<el-input v-model="form.redisName" maxlength="100" placeholder="请输入RedisName" />
</el-form-item>
</el-form>
<template #footer>
<el-button size="large" :icon="Close" @click="close">取消</el-button>
<el-button :loading="loading" size="large" :icon="Check" type="primary" @click="confirm">保存</el-button>
</template>
</app-edit-container>
</template>typescript
<script lang="ts" setup>
import { reactive, ref } from "vue"
import { FormInstance } from "element-plus"
import { HttpNames, IHttps } from "jingjian-https"
import { Global } from "jingjian-engine"
import { Form } from './CloudRedisForm'
import { UIFactory } from "jingjian-uicomponent-elementplus"
import { Check, Close } from '@element-plus/icons-vue'
const https = Global.ioc.get<IHttps>(HttpNames.key)
const formRef = ref<FormInstance>()
const form = ref<Form>(new Form())
const rules = reactive<any>(Form.toRules())
const container = ref()
const loading = ref(false)
const updateUrl = '/deployment/manager/cloudredis/update/accesskey'
const emits = defineEmits([ 'success' ])
const show = (entity?: any) => {
container.value.show()
form.value = new Form(entity)
}
const close = () => {
formRef?.value?.resetFields()
form.value = new Form();
container.value.close()
}
const success = () => {
close()
emits('success')
}
const confirm = async () => {
try
{
if(!formRef) return
if(!await formRef.value?.validate()) return
loading.value = true
let request = form.value.toRequest()
const result = await https.post(updateUrl, request);
if (!result.success) throw new Error(result.msg);
UIFactory.success('操作成功')
success()
}
catch (ex: any)
{
UIFactory.error(ex.message);
}
finally
{
loading.value = false
}
}
defineExpose({
show,
success
});
</script>typescript
// xxxEdit.ts 文件
import { FormItemRule, FormRules } from 'element-plus'
import { Arrayable } from 'element-plus/es/utils'
export class Form
{
id: string = ''
redisUserName: string = ''
redisPassword: string = ''
isEncrypted: boolean = true
get isEdit(): boolean
{
return !!this.id && this.id.length > 0;
}
constructor(entity?: any)
{
if(entity)
{
this.id = entity.id
this.redisUserName = entity.redisUserName
this.redisPassword = entity.redisPassword
}
}
toRequest()
{
return {
id: this.isEdit ? this.id: undefined,
redisUserName: this.redisUserName,
redisPassword: this.redisPassword,
isEncrypted: this.isEncrypted
}
}
static toRules(): Partial<Record<string, Arrayable<FormItemRule>>>
{
return {}
}
}- 界面校验规则,写入xxxEdit.ts文件中的toRules()方法中。
执行指令
- 当指令为"上传"时,则进入当前功能包的文件夹,并执行指令"npm run auto"