feat: restore admin form quality lightly
This commit is contained in:
@@ -100,6 +100,11 @@ const RoleSelector = defineAsyncComponent(() =>
|
|||||||
(res) => res.default,
|
(res) => res.default,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
const DeptSelector = defineAsyncComponent(() =>
|
||||||
|
import('#/components/zq-form/dept-selector/dept-selector.vue').then(
|
||||||
|
(res) => res.default,
|
||||||
|
),
|
||||||
|
);
|
||||||
const FileSelector = defineAsyncComponent(() =>
|
const FileSelector = defineAsyncComponent(() =>
|
||||||
import('#/components/zq-form/file-selector/file-selector.vue').then(
|
import('#/components/zq-form/file-selector/file-selector.vue').then(
|
||||||
(res) => res.default,
|
(res) => res.default,
|
||||||
@@ -115,6 +120,11 @@ const UserSelector = defineAsyncComponent(() =>
|
|||||||
(res) => res.default,
|
(res) => res.default,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
const PostSelector = defineAsyncComponent(() =>
|
||||||
|
import('#/components/zq-form/post-selector/post-selector.vue').then(
|
||||||
|
(res) => res.default,
|
||||||
|
),
|
||||||
|
);
|
||||||
const ZqIconPicker = defineAsyncComponent(() =>
|
const ZqIconPicker = defineAsyncComponent(() =>
|
||||||
import('#/components/zq-form/zq-icon-picker/zq-icon-picker.vue').then(
|
import('#/components/zq-form/zq-icon-picker/zq-icon-picker.vue').then(
|
||||||
(res) => res.default,
|
(res) => res.default,
|
||||||
@@ -166,12 +176,14 @@ export type ComponentType =
|
|||||||
| 'CheckboxGroup'
|
| 'CheckboxGroup'
|
||||||
| 'CodeEditor'
|
| 'CodeEditor'
|
||||||
| 'DatePicker'
|
| 'DatePicker'
|
||||||
|
| 'DeptSelector'
|
||||||
| 'Divider'
|
| 'Divider'
|
||||||
| 'FileSelector'
|
| 'FileSelector'
|
||||||
| 'IconPicker'
|
| 'IconPicker'
|
||||||
| 'ImageSelector'
|
| 'ImageSelector'
|
||||||
| 'Input'
|
| 'Input'
|
||||||
| 'InputNumber'
|
| 'InputNumber'
|
||||||
|
| 'PostSelector'
|
||||||
| 'RadioGroup'
|
| 'RadioGroup'
|
||||||
| 'RoleSelector'
|
| 'RoleSelector'
|
||||||
| 'Select'
|
| 'Select'
|
||||||
@@ -249,6 +261,7 @@ function initComponentAdapter() {
|
|||||||
PrimaryButton: (props, { attrs, slots }) => {
|
PrimaryButton: (props, { attrs, slots }) => {
|
||||||
return h(ElButton, { ...props, attrs, type: 'primary' }, slots);
|
return h(ElButton, { ...props, attrs, type: 'primary' }, slots);
|
||||||
},
|
},
|
||||||
|
DeptSelector: withDefaultPlaceholder(DeptSelector, 'select'),
|
||||||
RoleSelector: withDefaultPlaceholder(RoleSelector, 'select'),
|
RoleSelector: withDefaultPlaceholder(RoleSelector, 'select'),
|
||||||
Divider: ElDivider,
|
Divider: ElDivider,
|
||||||
FileSelector: withDefaultPlaceholder(FileSelector, 'select'),
|
FileSelector: withDefaultPlaceholder(FileSelector, 'select'),
|
||||||
@@ -261,6 +274,7 @@ function initComponentAdapter() {
|
|||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
}),
|
}),
|
||||||
InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'),
|
InputNumber: withDefaultPlaceholder(ElInputNumber, 'input'),
|
||||||
|
PostSelector: withDefaultPlaceholder(PostSelector, 'select'),
|
||||||
RadioGroup: (props, { attrs, slots }) => {
|
RadioGroup: (props, { attrs, slots }) => {
|
||||||
let defaultSlot;
|
let defaultSlot;
|
||||||
if (Reflect.has(slots, 'default')) {
|
if (Reflect.has(slots, 'default')) {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as RichTextEditor } from './rich-text-editor.vue';
|
||||||
@@ -0,0 +1,458 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { nextTick, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AlignCenter,
|
||||||
|
AlignLeft,
|
||||||
|
AlignRight,
|
||||||
|
Bold,
|
||||||
|
Eraser,
|
||||||
|
Heading1,
|
||||||
|
Image,
|
||||||
|
Italic,
|
||||||
|
Link,
|
||||||
|
List,
|
||||||
|
ListOrdered,
|
||||||
|
Quote,
|
||||||
|
Redo2,
|
||||||
|
Table,
|
||||||
|
Underline,
|
||||||
|
Undo2,
|
||||||
|
} from '@vben/icons';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ElButton,
|
||||||
|
ElButtonGroup,
|
||||||
|
ElColorPicker,
|
||||||
|
ElDivider,
|
||||||
|
ElInput,
|
||||||
|
ElMessage,
|
||||||
|
ElPopover,
|
||||||
|
ElSelect,
|
||||||
|
ElOption,
|
||||||
|
} from 'element-plus';
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
disabled?: boolean;
|
||||||
|
maxHeight?: number;
|
||||||
|
minHeight?: number;
|
||||||
|
modelValue?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
readonly?: boolean;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
maxHeight: 500,
|
||||||
|
minHeight: 200,
|
||||||
|
modelValue: '',
|
||||||
|
placeholder: '',
|
||||||
|
readonly: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
blur: [event: FocusEvent];
|
||||||
|
change: [value: string];
|
||||||
|
focus: [event: FocusEvent];
|
||||||
|
'update:modelValue': [value: string];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const editorRef = ref<HTMLElement>();
|
||||||
|
const linkPopoverVisible = ref(false);
|
||||||
|
const imagePopoverVisible = ref(false);
|
||||||
|
const tablePopoverVisible = ref(false);
|
||||||
|
const linkUrl = ref('');
|
||||||
|
const imageUrl = ref('');
|
||||||
|
const tableRows = ref(3);
|
||||||
|
const tableCols = ref(3);
|
||||||
|
const currentColor = ref('#1f2937');
|
||||||
|
const currentBlock = ref('P');
|
||||||
|
let isSyncing = false;
|
||||||
|
|
||||||
|
function syncHtml(value = props.modelValue || '') {
|
||||||
|
if (!editorRef.value || editorRef.value.innerHTML === value) return;
|
||||||
|
isSyncing = true;
|
||||||
|
editorRef.value.innerHTML = value;
|
||||||
|
nextTick(() => {
|
||||||
|
isSyncing = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(value) => syncHtml(value || ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
function emitChange() {
|
||||||
|
if (isSyncing || !editorRef.value) return;
|
||||||
|
const html = editorRef.value.innerHTML;
|
||||||
|
emit('update:modelValue', html);
|
||||||
|
emit('change', html);
|
||||||
|
}
|
||||||
|
|
||||||
|
function focusEditor() {
|
||||||
|
editorRef.value?.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function exec(command: string, value?: string) {
|
||||||
|
if (props.disabled || props.readonly) return;
|
||||||
|
focusEditor();
|
||||||
|
document.execCommand(command, false, value);
|
||||||
|
emitChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyBlock(value: string) {
|
||||||
|
currentBlock.value = value;
|
||||||
|
exec('formatBlock', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyColor(value: string | null) {
|
||||||
|
if (!value) return;
|
||||||
|
currentColor.value = value;
|
||||||
|
exec('foreColor', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertLink() {
|
||||||
|
const url = linkUrl.value.trim();
|
||||||
|
if (!url) {
|
||||||
|
ElMessage.warning('请输入链接地址');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
exec('createLink', url);
|
||||||
|
linkPopoverVisible.value = false;
|
||||||
|
linkUrl.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertImage() {
|
||||||
|
const url = imageUrl.value.trim();
|
||||||
|
if (!url) {
|
||||||
|
ElMessage.warning('请输入图片地址');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
exec('insertHTML', `<img src="${url}" alt="" />`);
|
||||||
|
imagePopoverVisible.value = false;
|
||||||
|
imageUrl.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertTable() {
|
||||||
|
const rows = Math.max(1, Math.min(10, tableRows.value));
|
||||||
|
const cols = Math.max(1, Math.min(8, tableCols.value));
|
||||||
|
const cells = Array.from({ length: cols })
|
||||||
|
.map(() => '<td><br></td>')
|
||||||
|
.join('');
|
||||||
|
const body = Array.from({ length: rows })
|
||||||
|
.map(() => `<tr>${cells}</tr>`)
|
||||||
|
.join('');
|
||||||
|
exec('insertHTML', `<table><tbody>${body}</tbody></table><p><br></p>`);
|
||||||
|
tablePopoverVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePaste(event: ClipboardEvent) {
|
||||||
|
const html = event.clipboardData?.getData('text/html');
|
||||||
|
const text = event.clipboardData?.getData('text/plain');
|
||||||
|
if (!html && !text) return;
|
||||||
|
event.preventDefault();
|
||||||
|
exec('insertHTML', html || (text || '').replaceAll('\n', '<br>'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFocus(event: FocusEvent) {
|
||||||
|
emit('focus', event);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBlur(event: FocusEvent) {
|
||||||
|
emitChange();
|
||||||
|
emit('blur', event);
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTick(() => syncHtml());
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="rich-text-editor" :class="{ 'is-disabled': disabled || readonly }">
|
||||||
|
<div class="rich-text-toolbar">
|
||||||
|
<ElSelect
|
||||||
|
v-model="currentBlock"
|
||||||
|
size="small"
|
||||||
|
class="block-select"
|
||||||
|
:disabled="disabled || readonly"
|
||||||
|
@change="applyBlock"
|
||||||
|
>
|
||||||
|
<ElOption label="正文" value="P" />
|
||||||
|
<ElOption label="标题 1" value="H1" />
|
||||||
|
<ElOption label="标题 2" value="H2" />
|
||||||
|
<ElOption label="标题 3" value="H3" />
|
||||||
|
</ElSelect>
|
||||||
|
|
||||||
|
<ElDivider direction="vertical" />
|
||||||
|
|
||||||
|
<ElButtonGroup>
|
||||||
|
<ElButton
|
||||||
|
:icon="Undo2"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
title="撤销"
|
||||||
|
@click="exec('undo')"
|
||||||
|
/>
|
||||||
|
<ElButton
|
||||||
|
:icon="Redo2"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
title="重做"
|
||||||
|
@click="exec('redo')"
|
||||||
|
/>
|
||||||
|
</ElButtonGroup>
|
||||||
|
|
||||||
|
<ElDivider direction="vertical" />
|
||||||
|
|
||||||
|
<ElButtonGroup>
|
||||||
|
<ElButton
|
||||||
|
:icon="Bold"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
title="加粗"
|
||||||
|
@click="exec('bold')"
|
||||||
|
/>
|
||||||
|
<ElButton
|
||||||
|
:icon="Italic"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
title="斜体"
|
||||||
|
@click="exec('italic')"
|
||||||
|
/>
|
||||||
|
<ElButton
|
||||||
|
:icon="Underline"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
title="下划线"
|
||||||
|
@click="exec('underline')"
|
||||||
|
/>
|
||||||
|
</ElButtonGroup>
|
||||||
|
|
||||||
|
<ElDivider direction="vertical" />
|
||||||
|
|
||||||
|
<ElButtonGroup>
|
||||||
|
<ElButton
|
||||||
|
:icon="AlignLeft"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
title="左对齐"
|
||||||
|
@click="exec('justifyLeft')"
|
||||||
|
/>
|
||||||
|
<ElButton
|
||||||
|
:icon="AlignCenter"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
title="居中"
|
||||||
|
@click="exec('justifyCenter')"
|
||||||
|
/>
|
||||||
|
<ElButton
|
||||||
|
:icon="AlignRight"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
title="右对齐"
|
||||||
|
@click="exec('justifyRight')"
|
||||||
|
/>
|
||||||
|
</ElButtonGroup>
|
||||||
|
|
||||||
|
<ElDivider direction="vertical" />
|
||||||
|
|
||||||
|
<ElButtonGroup>
|
||||||
|
<ElButton
|
||||||
|
:icon="List"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
title="无序列表"
|
||||||
|
@click="exec('insertUnorderedList')"
|
||||||
|
/>
|
||||||
|
<ElButton
|
||||||
|
:icon="ListOrdered"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
title="有序列表"
|
||||||
|
@click="exec('insertOrderedList')"
|
||||||
|
/>
|
||||||
|
<ElButton
|
||||||
|
:icon="Quote"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
title="引用"
|
||||||
|
@click="exec('formatBlock', 'BLOCKQUOTE')"
|
||||||
|
/>
|
||||||
|
</ElButtonGroup>
|
||||||
|
|
||||||
|
<ElDivider direction="vertical" />
|
||||||
|
|
||||||
|
<ElColorPicker
|
||||||
|
v-model="currentColor"
|
||||||
|
size="small"
|
||||||
|
:disabled="disabled || readonly"
|
||||||
|
@change="applyColor"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ElDivider direction="vertical" />
|
||||||
|
|
||||||
|
<ElPopover v-model:visible="linkPopoverVisible" width="300" trigger="click">
|
||||||
|
<template #reference>
|
||||||
|
<ElButton :icon="Link" size="small" text title="插入链接" />
|
||||||
|
</template>
|
||||||
|
<div class="popover-form">
|
||||||
|
<ElInput v-model="linkUrl" placeholder="https://example.com" />
|
||||||
|
<ElButton type="primary" size="small" @click="insertLink">插入链接</ElButton>
|
||||||
|
</div>
|
||||||
|
</ElPopover>
|
||||||
|
|
||||||
|
<ElPopover v-model:visible="imagePopoverVisible" width="300" trigger="click">
|
||||||
|
<template #reference>
|
||||||
|
<ElButton :icon="Image" size="small" text title="插入图片" />
|
||||||
|
</template>
|
||||||
|
<div class="popover-form">
|
||||||
|
<ElInput v-model="imageUrl" placeholder="图片 URL" />
|
||||||
|
<ElButton type="primary" size="small" @click="insertImage">插入图片</ElButton>
|
||||||
|
</div>
|
||||||
|
</ElPopover>
|
||||||
|
|
||||||
|
<ElPopover v-model:visible="tablePopoverVisible" width="260" trigger="click">
|
||||||
|
<template #reference>
|
||||||
|
<ElButton :icon="Table" size="small" text title="插入表格" />
|
||||||
|
</template>
|
||||||
|
<div class="table-popover">
|
||||||
|
<ElInput v-model.number="tableRows" type="number" min="1" max="10">
|
||||||
|
<template #prepend>行</template>
|
||||||
|
</ElInput>
|
||||||
|
<ElInput v-model.number="tableCols" type="number" min="1" max="8">
|
||||||
|
<template #prepend>列</template>
|
||||||
|
</ElInput>
|
||||||
|
<ElButton type="primary" size="small" @click="insertTable">插入表格</ElButton>
|
||||||
|
</div>
|
||||||
|
</ElPopover>
|
||||||
|
|
||||||
|
<ElButton
|
||||||
|
:icon="Eraser"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
title="清除格式"
|
||||||
|
@click="exec('removeFormat')"
|
||||||
|
/>
|
||||||
|
<ElButton
|
||||||
|
:icon="Heading1"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
title="标题"
|
||||||
|
@click="applyBlock('H2')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
ref="editorRef"
|
||||||
|
class="rich-text-content"
|
||||||
|
:contenteditable="!disabled && !readonly"
|
||||||
|
:data-placeholder="placeholder"
|
||||||
|
:style="{ minHeight: `${minHeight}px`, maxHeight: `${maxHeight}px` }"
|
||||||
|
role="textbox"
|
||||||
|
aria-multiline="true"
|
||||||
|
@blur="handleBlur"
|
||||||
|
@focus="handleFocus"
|
||||||
|
@input="emitChange"
|
||||||
|
@paste="handlePaste"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.rich-text-editor {
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--el-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-editor.is-disabled {
|
||||||
|
opacity: 0.72;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-toolbar {
|
||||||
|
display: flex;
|
||||||
|
min-height: 42px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||||
|
padding: 5px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-select {
|
||||||
|
width: 96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-content {
|
||||||
|
overflow: auto;
|
||||||
|
padding: 12px 14px;
|
||||||
|
outline: none;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-content:empty::before {
|
||||||
|
color: var(--el-text-color-placeholder);
|
||||||
|
content: attr(data-placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-content :deep(h1),
|
||||||
|
.rich-text-content :deep(h2),
|
||||||
|
.rich-text-content :deep(h3) {
|
||||||
|
margin: 0.8em 0 0.45em;
|
||||||
|
font-weight: 650;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-content :deep(h1) {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-content :deep(h2) {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-content :deep(h3) {
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-content :deep(p) {
|
||||||
|
margin: 0.45em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-content :deep(blockquote) {
|
||||||
|
margin: 8px 0;
|
||||||
|
border-left: 4px solid var(--el-border-color);
|
||||||
|
padding-left: 14px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-content :deep(img) {
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-content :deep(table) {
|
||||||
|
width: 100%;
|
||||||
|
margin: 8px 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-content :deep(td),
|
||||||
|
.rich-text-content :deep(th) {
|
||||||
|
min-width: 48px;
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
padding: 6px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-content :deep(a) {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-form,
|
||||||
|
.table-popover {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -60,6 +60,7 @@ import {
|
|||||||
import { FuPage } from '#/components/fu-page';
|
import { FuPage } from '#/components/fu-page';
|
||||||
import { UserAvatar } from '#/components/user-avatar';
|
import { UserAvatar } from '#/components/user-avatar';
|
||||||
import { ZqDrawer } from '#/components/zq-drawer';
|
import { ZqDrawer } from '#/components/zq-drawer';
|
||||||
|
import { RichTextEditor } from '#/components/zq-form/rich-text-editor';
|
||||||
|
|
||||||
defineOptions({ name: 'AnnouncementManager' });
|
defineOptions({ name: 'AnnouncementManager' });
|
||||||
|
|
||||||
@@ -810,13 +811,12 @@ onBeforeUnmount(() => {
|
|||||||
prop="content"
|
prop="content"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<ElInput
|
<RichTextEditor
|
||||||
v-model="formData.content"
|
v-model="formData.content"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
type="textarea"
|
|
||||||
:placeholder="$t('announcement.formContentPlaceholder')"
|
:placeholder="$t('announcement.formContentPlaceholder')"
|
||||||
:autosize="{ minRows: 8, maxRows: 16 }"
|
:min-height="220"
|
||||||
resize="vertical"
|
:max-height="520"
|
||||||
/>
|
/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
</ElForm>
|
</ElForm>
|
||||||
|
|||||||
@@ -191,6 +191,14 @@ export function getFormSchema(): VbenFormSchema[] {
|
|||||||
fieldName: 'bio',
|
fieldName: 'bio',
|
||||||
label: $t('user.bio'),
|
label: $t('user.bio'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: 'DeptSelector',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: $t('user.selectDept'),
|
||||||
|
},
|
||||||
|
fieldName: 'dept_id',
|
||||||
|
label: $t('user.dept'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: 'UserSelector',
|
component: 'UserSelector',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
@@ -199,6 +207,14 @@ export function getFormSchema(): VbenFormSchema[] {
|
|||||||
fieldName: 'manager_id',
|
fieldName: 'manager_id',
|
||||||
label: $t('user.manager'),
|
label: $t('user.manager'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: 'PostSelector',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: $t('user.selectPost'),
|
||||||
|
},
|
||||||
|
fieldName: 'post_id',
|
||||||
|
label: $t('user.post'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: 'RoleSelector',
|
component: 'RoleSelector',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
@@ -295,6 +311,12 @@ export function useZqTableColumns(): Column[] {
|
|||||||
title: $t('user.city'),
|
title: $t('user.city'),
|
||||||
width: 120,
|
width: 120,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'dept_name',
|
||||||
|
dataKey: 'dept_name',
|
||||||
|
title: $t('user.dept'),
|
||||||
|
width: 140,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'manager_name',
|
key: 'manager_name',
|
||||||
dataKey: 'manager_name',
|
dataKey: 'manager_name',
|
||||||
|
|||||||
Reference in New Issue
Block a user