forked from mengyxu/noob-components
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
6.5 KiB
263 lines
6.5 KiB
|
3 months ago
|
# Views Components
|
||
|
|
|
||
|
|
> View-level CRUD management components.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
Views are page-level components that provide full CRUD operations for domain entities. Each view follows a consistent pattern using shared components from `noob-mengyxu`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Components
|
||
|
|
|
||
|
|
| Component | File | Purpose |
|
||
|
|
|-----------|------|---------|
|
||
|
|
| User | `views/user.vue` | User management with password reset |
|
||
|
|
| Role | `views/role.vue` | Role management with permission assignment |
|
||
|
|
| Dictionary | `views/dictionary.vue` | Hierarchical dictionary with lazy loading |
|
||
|
|
| Config | `views/config.vue` | Configuration management |
|
||
|
|
| Buffer | `views/buffer.vue` | Buffer/warning management |
|
||
|
|
| Permission | `views/permission.vue` | Permission tree management |
|
||
|
|
| Status | `views/status.vue` | Status management |
|
||
|
|
| Log | `views/log.vue` | Log viewing |
|
||
|
|
| Scope | `views/scope.vue` | Scope management |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Standard View Pattern
|
||
|
|
|
||
|
|
All view components follow the same structure:
|
||
|
|
|
||
|
|
```vue
|
||
|
|
<template>
|
||
|
|
<SearchRow @query="query" @add="addItem">
|
||
|
|
<!-- Optional filter inputs -->
|
||
|
|
</SearchRow>
|
||
|
|
<ListTable :props="props" :data="result">
|
||
|
|
<template #action="{ row }">
|
||
|
|
<TableAction @modify="modify" @del="delate">
|
||
|
|
<!-- Optional extra actions -->
|
||
|
|
</TableAction>
|
||
|
|
</template>
|
||
|
|
</ListTable>
|
||
|
|
|
||
|
|
<el-dialog v-model="flag.modify">
|
||
|
|
<ModifyForm :param="item" :rules="rules" :items="items" @confirm="confirm" />
|
||
|
|
</el-dialog>
|
||
|
|
</template>
|
||
|
|
```
|
||
|
|
|
||
|
|
### Key Imports
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { Api, ListTable, SearchRow, NoobInput, NoobSelect,
|
||
|
|
TableAction, ModifyForm, Element, PageExample, PageResult } from "noob-mengyxu";
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Props Configuration
|
||
|
|
|
||
|
|
Column definitions using i18n keys:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const props = [
|
||
|
|
{ i18n: 'user.prop.0', code: 'userId', width: 120 }, // i18n key, data field, width
|
||
|
|
{ i18n: 'user.prop.3', code: 'name', width: 120 },
|
||
|
|
{ i18n: 'user.prop.4', code: 'phone', width: 180 },
|
||
|
|
{ i18n: 'user.prop.7', code: 'action', slot: true, width: 180, fixed: 'right' }
|
||
|
|
]
|
||
|
|
```
|
||
|
|
|
||
|
|
### Column Options
|
||
|
|
|
||
|
|
| Option | Type | Description |
|
||
|
|
|--------|------|-------------|
|
||
|
|
| `i18n` | string | i18n key for column header |
|
||
|
|
| `code` | string | Field name in data object |
|
||
|
|
| `width` | number | Column width in pixels |
|
||
|
|
| `slot` | boolean | Enable slot for custom rendering |
|
||
|
|
| `fixed` | string | Fix column position ('right', 'left') |
|
||
|
|
| `dict` | string | Dictionary key for value translation |
|
||
|
|
| `type` | string | Column type (e.g., 'action') |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Form Items Configuration
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const items = [
|
||
|
|
{ i18n: 'user.prop.0', code: 'userId', slot: true }, // slot = custom input
|
||
|
|
{ i18n: 'user.prop.1', code: 'password', type: 'password', noModify: true },
|
||
|
|
{ i18n: 'user.prop.2', code: 'roles', slot: true },
|
||
|
|
{ i18n: 'user.prop.3', code: 'name' }
|
||
|
|
]
|
||
|
|
```
|
||
|
|
|
||
|
|
### Item Options
|
||
|
|
|
||
|
|
| Option | Type | Description |
|
||
|
|
|--------|------|-------------|
|
||
|
|
| `i18n` | string | i18n key for label |
|
||
|
|
| `code` | string | Field name |
|
||
|
|
| `slot` | boolean | Custom input via slot |
|
||
|
|
| `type` | string | Input type (password, etc.) |
|
||
|
|
| `noModify` | boolean | Skip in modify mode |
|
||
|
|
| `dict` | string | Dictionary for select options |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Rules Configuration
|
||
|
|
|
||
|
|
Validation rules using Element validators:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const { SimpleRequired, Password, Username, Name, IdCard, Phone, Email } = Element;
|
||
|
|
|
||
|
|
const rules = {
|
||
|
|
userId: [new Username()],
|
||
|
|
password: [new Password()],
|
||
|
|
roles: [{type: 'array', required:true, message: t('rule.pleaseSelect') + t('user.prop.2'), trigger: 'blur'}],
|
||
|
|
name: [new SimpleRequired('user.prop.3'), new Name()],
|
||
|
|
idCard: [new IdCard()],
|
||
|
|
phone: [new Phone()],
|
||
|
|
email: [new Email()]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## CRUD Operations Pattern
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const { list, add, set, del } = Api.user;
|
||
|
|
const result = ref(new PageResult());
|
||
|
|
const example = reactive<any>(new PageExample());
|
||
|
|
|
||
|
|
const query = () => {
|
||
|
|
list(example).then((rsp: any) => result.value = rsp)
|
||
|
|
}
|
||
|
|
|
||
|
|
const addItem = () => {
|
||
|
|
item.value = {};
|
||
|
|
form.value?.clearValidate();
|
||
|
|
flag.add = true;
|
||
|
|
flag.modify = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
const modify = (row) => {
|
||
|
|
item.value = JSON.parse(JSON.stringify(row));
|
||
|
|
form.value?.clearValidate();
|
||
|
|
flag.add = false;
|
||
|
|
flag.modify = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
const delate = (row) => {
|
||
|
|
Element.confirm(t('delete.0'), t('delete.1')).then(() => {
|
||
|
|
del(row).then(query);
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
const confirm = () => {
|
||
|
|
const api = flag.add ? add : set;
|
||
|
|
api(item.value).then(rsp => {
|
||
|
|
if (rsp) {
|
||
|
|
query();
|
||
|
|
flag.modify = false;
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
onMounted(() => {
|
||
|
|
query();
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Code Examples
|
||
|
|
|
||
|
|
### User View (`views/user.vue`)
|
||
|
|
|
||
|
|
User management with role assignment and password reset.
|
||
|
|
|
||
|
|
Key features:
|
||
|
|
- User CRUD with md5 password hashing
|
||
|
|
- Role multi-select
|
||
|
|
- Reset password action
|
||
|
|
- Action buttons with status check (`row.status != 'R'`)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const { list, add, set, del, reset } = Api.user;
|
||
|
|
const { roles } = useStore().state;
|
||
|
|
|
||
|
|
// Reset password flow
|
||
|
|
const resetPass = row => {
|
||
|
|
Element.confirm(t('user.reset.1'), t('user.reset.2')).then(() => {
|
||
|
|
reset(row);
|
||
|
|
})
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Role View (`views/role.vue`)
|
||
|
|
|
||
|
|
Role management with permission tree assignment.
|
||
|
|
|
||
|
|
Key features:
|
||
|
|
- Permission tree with `el-tree`
|
||
|
|
- Cascading check with `check-strictly`
|
||
|
|
- Vuex state update on role changes
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const { tree } = Api.permission;
|
||
|
|
|
||
|
|
const permission = (row) => {
|
||
|
|
role.value = JSON.parse(JSON.stringify(row));
|
||
|
|
permissionTree.value?.setCheckedNodes(role.value.permissions);
|
||
|
|
flag.per = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
const setPermission = () => {
|
||
|
|
role.value.permissions = permissionTree.value?.getCheckedKeys();
|
||
|
|
set(role.value).then(rsp => {
|
||
|
|
if (rsp) {
|
||
|
|
query();
|
||
|
|
flag.per = false;
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Dictionary View (`views/dictionary.vue`)
|
||
|
|
|
||
|
|
Hierarchical dictionary with lazy loading.
|
||
|
|
|
||
|
|
Key features:
|
||
|
|
- Lazy loading with `rowKey` and `lazy`
|
||
|
|
- Tree structure transformation in query callback
|
||
|
|
- Parent-child relationship support
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const query = () => {
|
||
|
|
list(example).then((rsp: any) => {
|
||
|
|
result.value = rsp;
|
||
|
|
result.value.data.forEach((i: any) => {
|
||
|
|
i.child = i.children;
|
||
|
|
i.children = [];
|
||
|
|
i.hasChildren = true;
|
||
|
|
})
|
||
|
|
})
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Anti-Patterns
|
||
|
|
|
||
|
|
1. **Do not use `window.history.length` for navigation** - Use Vue Router properly
|
||
|
|
2. **Do not skip `form.value?.clearValidate()`** - Always clear validation on open
|
||
|
|
3. **Do not mutate props directly** - Use reactive refs
|
||
|
|
4. **Do not forget `onMounted` query** - Data should load on mount
|