Skip to content

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"

广州宝点数字化科技