| | |
| | | } from 'ant-design-vue'; |
| | | |
| | | import { Tinymce as RichTextarea } from '#/components/tinymce'; |
| | | import { FileUpload, ImageUpload } from '#/components/upload'; |
| | | import { FileUpload, ImageUpload, NewFileUpload } from '#/components/upload'; |
| | | |
| | | const withDefaultPlaceholder = <T extends Component>( |
| | | component: T, |
| | |
| | | | 'DefaultButton' |
| | | | 'Divider' |
| | | | 'FileUpload' |
| | | | 'NewFileUpload' |
| | | | 'IconPicker' |
| | | | 'ImageUpload' |
| | | | 'Input' |
| | |
| | | Upload, |
| | | ImageUpload, |
| | | FileUpload, |
| | | NewFileUpload, |
| | | RichTextarea, |
| | | }; |
| | | |
| | |
| | | export { default as FileUpload } from './src/file-upload.vue'; |
| | | export { default as NewFileUpload } from './src/new-file-upload.vue'; |
| | | export { default as ImageUpload } from './src/image-upload.vue'; |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <!-- è¿æ¯ä¸ä¸ªç¨æ¥èªå®ä¹åçæä»¶ä¸ä¼ ç»ä»¶ --> |
| | | <script lang="ts" setup> |
| | | import type { UploadFile, UploadProps } from 'ant-design-vue'; |
| | | import type { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface'; |
| | | |
| | | import type { AxiosResponse } from '@vben/request'; |
| | | |
| | | import type { AxiosProgressEvent } from '#/api'; |
| | | |
| | | import { ref, toRefs, watch } from 'vue'; |
| | | |
| | | import { $t } from '@vben/locales'; |
| | | |
| | | import { UploadOutlined } from '@ant-design/icons-vue'; |
| | | import { message, Upload } from 'ant-design-vue'; |
| | | import { isArray, isFunction, isObject, isString } from 'lodash-es'; |
| | | |
| | | import { uploadApi } from '#/api'; |
| | | |
| | | import { checkFileType } from './helper'; |
| | | import { UploadResultStatus } from './typing'; |
| | | import { useUploadType } from './use-upload'; |
| | | |
| | | defineOptions({ name: 'FileUpload', inheritAttrs: false }); |
| | | |
| | | const props = withDefaults( |
| | | defineProps<{ |
| | | /** |
| | | * å»ºè®®ä½¿ç¨æå±å(ä¸å¸¦.) |
| | | * æè
æä»¶å¤´ image/pngç(æµè¯å¤æä¸åç¡®) 䏿¯æimage/*类似çåæ³ |
| | | * éèªè¡æ¹é ./helper/checkFileTypeæ¹æ³ |
| | | */ |
| | | accept?: string[]; |
| | | api?: ( |
| | | file: Blob | File, |
| | | onUploadProgress?: AxiosProgressEvent, |
| | | ) => Promise<AxiosResponse<any>>; |
| | | disabled?: boolean; |
| | | helpText?: string; |
| | | // æå¤§æ°éçæä»¶ï¼Infinityä¸éå¶ |
| | | maxNumber?: number; |
| | | // æä»¶æå¤§å¤å°MB |
| | | maxSize?: number; |
| | | // æ¯å¦æ¯æå¤é |
| | | multiple?: boolean; |
| | | // support xxx.xxx.xx |
| | | // è¿åçåæ®µ é»è®¤url |
| | | resultField?: 'fileName' | 'ossId' | 'url' | string; |
| | | /** |
| | | * æ¯å¦æ¾ç¤ºä¸é¢çæè¿° |
| | | */ |
| | | showDescription?: boolean; |
| | | value?: string[]; |
| | | }>(), |
| | | { |
| | | value: () => [], |
| | | disabled: false, |
| | | helpText: '', |
| | | maxSize: 2, |
| | | maxNumber: 1, |
| | | accept: () => [], |
| | | multiple: false, |
| | | api: uploadApi, |
| | | resultField: '', |
| | | showDescription: true, |
| | | }, |
| | | ); |
| | | const emit = defineEmits(['change', 'update:value', 'delete', 'startUpload', 'EndUpload']); |
| | | const { accept, helpText, maxNumber, maxSize } = toRefs(props); |
| | | const isInnerOperate = ref<boolean>(false); |
| | | const { getStringAccept } = useUploadType({ |
| | | acceptRef: accept, |
| | | helpTextRef: helpText, |
| | | maxNumberRef: maxNumber, |
| | | maxSizeRef: maxSize, |
| | | }); |
| | | |
| | | const fileList = ref<UploadProps['fileList']>([]); |
| | | const isLtMsg = ref<boolean>(true); |
| | | const isActMsg = ref<boolean>(true); |
| | | const isFirstRender = ref<boolean>(true); |
| | | |
| | | watch( |
| | | () => props.value, |
| | | (v) => { |
| | | if (isInnerOperate.value) { |
| | | isInnerOperate.value = false; |
| | | return; |
| | | } |
| | | let value: string[] = []; |
| | | if (v) { |
| | | if (isArray(v)) { |
| | | value = v; |
| | | } else { |
| | | value.push(v); |
| | | } |
| | | fileList.value = value.map((item, i) => { |
| | | if (item && isString(item)) { |
| | | return { |
| | | uid: `${-i}`, |
| | | name: item.slice(Math.max(0, item.lastIndexOf('/') + 1)), |
| | | status: 'done', |
| | | url: item, |
| | | }; |
| | | } else if (item && isObject(item)) { |
| | | return item; |
| | | } |
| | | return null; |
| | | }) as UploadProps['fileList']; |
| | | } |
| | | if (!isFirstRender.value) { |
| | | emit('change', value); |
| | | isFirstRender.value = false; |
| | | } |
| | | }, |
| | | { |
| | | immediate: true, |
| | | deep: true, |
| | | }, |
| | | ); |
| | | |
| | | const handleRemove = async (file: UploadFile) => { |
| | | if (fileList.value) { |
| | | const index = fileList.value.findIndex((item) => item.uid === file.uid); |
| | | index !== -1 && fileList.value.splice(index, 1); |
| | | const value = getValue(); |
| | | isInnerOperate.value = true; |
| | | emit('update:value', value); |
| | | emit('change', value); |
| | | } |
| | | emit('delete', file); |
| | | }; |
| | | |
| | | const beforeUpload = async (file: File) => { |
| | | const { maxSize, accept } = props; |
| | | const isAct = await checkFileType(file, accept); |
| | | if (!isAct) { |
| | | message.error($t('component.upload.acceptUpload', [accept])); |
| | | isActMsg.value = false; |
| | | // 鲿¢å¼¹åºå¤ä¸ªé误æç¤º |
| | | setTimeout(() => (isActMsg.value = true), 1000); |
| | | } |
| | | const isLt = file.size / 1024 / 1024 > maxSize; |
| | | if (isLt) { |
| | | message.error($t('component.upload.maxSizeMultiple', [maxSize])); |
| | | isLtMsg.value = false; |
| | | // 鲿¢å¼¹åºå¤ä¸ªé误æç¤º |
| | | setTimeout(() => (isLtMsg.value = true), 1000); |
| | | } |
| | | return (isAct && !isLt) || Upload.LIST_IGNORE; |
| | | }; |
| | | |
| | | async function customRequest(info: UploadRequestOption<any>) { |
| | | const { api } = props; |
| | | if (!api || !isFunction(api)) { |
| | | console.warn('upload api must exist and be a function'); |
| | | return; |
| | | } |
| | | emit( 'startUpload' ) |
| | | try { |
| | | // è¿åº¦æ¡äºä»¶ |
| | | const progressEvent: AxiosProgressEvent = (e) => { |
| | | const percent = Math.trunc((e.loaded / e.total!) * 100); |
| | | info.onProgress!({ percent }); |
| | | }; |
| | | const res = await api?.(info.file as File, progressEvent); |
| | | /** |
| | | * ç±getValueå¤ç ä¼ å¯¹è±¡è¿å» |
| | | * ç´æ¥ä¼ string(id)ä¼è¢«è½¬ä¸ºNumber |
| | | * å
é¨çé»è¾ç±requestClient.uploadå¤ç è¿éä¸ç¨å¤æä¸å¡ç¶æç ä¸ç¬¦åä¼èªå¨reject |
| | | */ |
| | | info.onSuccess!(res); |
| | | message.success($t('component.upload.uploadSuccess')); |
| | | // è·å |
| | | const value = getValue(); |
| | | isInnerOperate.value = true; |
| | | emit('update:value', value); |
| | | emit('change', value); |
| | | } catch (error: any) { |
| | | console.error(error); |
| | | info.onError!(error); |
| | | } finally { |
| | | emit( 'EndUpload' ) |
| | | } |
| | | } |
| | | |
| | | function getValue() { |
| | | const list = (fileList.value || []) |
| | | .filter((item) => item?.status === UploadResultStatus.DONE) |
| | | .map((item: any) => { |
| | | if (item?.response && props?.resultField) { |
| | | return item?.response?.[props.resultField]; |
| | | } |
| | | // éç¨äºå·²ç»æå¾ç åæ¾çæ
åµ ä¼é»è®¤å¨initå¤ç为{url: 'xx'} |
| | | if (item?.url) { |
| | | return item.url; |
| | | } |
| | | // 注æè¿éåçkey为 url |
| | | return item?.response?.url; |
| | | }); |
| | | return list; |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <div> |
| | | <Upload |
| | | v-bind="$attrs" |
| | | v-model:file-list="fileList" |
| | | :accept="getStringAccept" |
| | | :before-upload="beforeUpload" |
| | | :custom-request="customRequest" |
| | | :disabled="disabled" |
| | | :max-count="maxNumber" |
| | | :multiple="multiple" |
| | | list-type="text" |
| | | :progress="{ showInfo: true }" |
| | | @remove="handleRemove" |
| | | > |
| | | <div v-if="fileList && fileList.length < maxNumber"> |
| | | <a-button> |
| | | <UploadOutlined /> |
| | | {{ $t('component.upload.upload') }} |
| | | </a-button> |
| | | </div> |
| | | <div v-if="showDescription" class="mt-2 flex flex-wrap items-center"> |
| | | 请ä¸ä¼ ä¸è¶
è¿ |
| | | <div class="text-primary mx-1 font-bold">{{ maxSize }}MB</div> |
| | | ç |
| | | <div class="text-primary mx-1 font-bold">{{ accept.join('/') }}</div> |
| | | æ ¼å¼æä»¶ |
| | | </div> |
| | | </Upload> |
| | | </div> |
| | | </template> |
| | | |
| | | <style> |
| | | .ant-upload-select-picture-card i { |
| | | font-size: 32px; |
| | | color: #999; |
| | | } |
| | | |
| | | .ant-upload-select-picture-card .ant-upload-text { |
| | | margin-top: 8px; |
| | | color: #666; |
| | | } |
| | | </style> |
| | |
| | | { |
| | | "common": { |
| | | "add": "Add", |
| | | "WorkIssued": "WorkIssued", |
| | | "edit": "Edit", |
| | | "look": "Look", |
| | | "report":"report", |
| | | "AssigningWork":"Assigning Work", |
| | | "delete": "Delete", |
| | | "more": "More", |
| | | "search": "Search", |
| | |
| | | { |
| | | "common": { |
| | | "add": "æ°å¢", |
| | | "WorkIssued": "å·¥ä½ä¸å", |
| | | "edit": "ç¼è¾", |
| | | "look": "æ¥ç", |
| | | "report":"æ±æ¥", |
| | | "AssigningWork":"å·¥ä½ä¸å", |
| | | "delete": "å é¤", |
| | | "more": "æ´å¤", |
| | | "search": "æç´¢", |
¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { UploadFile, UploadProps } from 'ant-design-vue'; |
| | | import type { FormSchemaGetter } from '#/adapter/form'; |
| | | import type { VxeGridProps } from '#/adapter/vxe-table'; |
| | | |
| | | import { DictEnum } from '@vben/constants'; |
| | | import { getPopupContainer } from '@vben/utils'; |
| | | import { useAppConfig } from '@vben/hooks'; |
| | | import { useAccessStore } from '@vben/stores'; |
| | | import { ref } from 'vue'; |
| | | import { getDictOptions } from '#/utils/dict'; |
| | | // import { OptionsItem } from "" |
| | | |
| | | export const querySchema: FormSchemaGetter = () => [ |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'workName', |
| | | label: 'å·¥ä½åç§°', |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'id', |
| | | label: 'ç¼å·', |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'responsibleDepartment', |
| | | label: 'è´è´£é¨é¨', |
| | | }, |
| | | { |
| | | component: 'Select', |
| | | componentProps: { |
| | | getPopupContainer, |
| | | options: getDictOptions(DictEnum.TASK_STATUS), |
| | | }, |
| | | fieldName: 'taskStatus', |
| | | label: 'ä»»å¡ç¶æ', |
| | | }, |
| | | { |
| | | component: 'Select', |
| | | componentProps: { |
| | | getPopupContainer, |
| | | options: getDictOptions(DictEnum.ASSIGNMENT_STATUS), |
| | | }, |
| | | fieldName: 'assignmentStatus', |
| | | label: 'åé
ç¶æ', |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'Annual', |
| | | label: '年度', |
| | | }, |
| | | ]; |
| | | |
| | | export const columns: VxeGridProps['columns'] = [ |
| | | { type: 'checkbox', width: 60 }, |
| | | { |
| | | field: 'workName', |
| | | title: 'åç§°', |
| | | }, |
| | | { |
| | | field: 'id', |
| | | title: 'ç¼å·', |
| | | }, |
| | | { |
| | | field: 'responsibleDepartment', |
| | | title: 'è´è´£é¨é¨', |
| | | }, |
| | | { |
| | | field: 'Head', |
| | | title: 'è´è´£äºº', |
| | | }, |
| | | { |
| | | field: 'workClass', |
| | | title: 'ç±»å«', |
| | | }, |
| | | { |
| | | field: 'assessmentTime', |
| | | title: 'èæ ¸æ¶é´', |
| | | }, |
| | | { |
| | | field: 'taskStatus', |
| | | title: 'ä»»å¡ç¶æ', |
| | | }, |
| | | { |
| | | field: 'assignmentStatus', |
| | | title: 'åé
ç¶æ', |
| | | }, |
| | | { |
| | | field: 'Annual', |
| | | title: '年度', |
| | | }, |
| | | { |
| | | field: 'workProgress', |
| | | title: 'å·¥ä½è¿åº¦', |
| | | }, |
| | | { |
| | | field: 'action', |
| | | fixed: 'right', |
| | | slots: { default: 'action' }, |
| | | title: 'æä½', |
| | | width: 180, |
| | | }, |
| | | ]; |
| | | // 表å |
| | | export const drawerSchema: FormSchemaGetter = () => [ |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'workName', |
| | | label: 'å·¥ä½åç§°', |
| | | rules: 'required' |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'workClass', |
| | | label: 'å·¥ä½ç±»å«', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'Textarea', |
| | | fieldName: 'workContent', |
| | | label: 'å·¥ä½å
容' |
| | | }, |
| | | |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'projectBudget', |
| | | label: '项ç®é¢ç®%', |
| | | defaultValue: undefined |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'amountProject', |
| | | defaultValue: undefined, |
| | | label: '项ç®éé¢' |
| | | }, |
| | | { |
| | | component: 'Select', |
| | | componentProps: { |
| | | getPopupContainer, |
| | | options: [] |
| | | }, |
| | | fieldName: 'responsibleDepartment', |
| | | label: 'è´è´£é¨é¨', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'Select', |
| | | componentProps: { |
| | | getPopupContainer, |
| | | options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE) |
| | | }, |
| | | fieldName: 'Head', |
| | | label: 'è´è´£äºº', |
| | | }, |
| | | { |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | format: 'YYYY', |
| | | showTime: true, |
| | | valueFormat: 'YYYY', |
| | | picker: "year", |
| | | getPopupContainer, |
| | | }, |
| | | fieldName: 'Annual', |
| | | label: '年度', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | format: 'YYYY-MM-DD', |
| | | showTime: true, |
| | | valueFormat: 'YYYY-MM-DD', |
| | | getPopupContainer, |
| | | }, |
| | | fieldName: 'assessmentTime', |
| | | label: 'èæ ¸æ¶é´', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'Select', |
| | | fieldName: 'assessmentIndicators', |
| | | formItemClass: 'items-baseline', |
| | | label: 'èæ ¸ææ ', |
| | | }, |
| | | { |
| | | fieldName: 'File', |
| | | component: 'NewFileUpload', |
| | | componentProps: { |
| | | action: uploadUrl, |
| | | headers: headers, |
| | | change: handleChange, |
| | | maxSize: 20, |
| | | onDelete:(file: UploadFile)=>{ |
| | | console.log("åå¨æä»¶è¢«å é¤",file) |
| | | }, |
| | | onStartUpload:()=>{ |
| | | //å¼å§ä¸ä¼ |
| | | UploadFileState.value = true |
| | | }, |
| | | onEndUpload:()=>{ |
| | | //ä¸ä¼ ç»æ |
| | | UploadFileState.value = false |
| | | } |
| | | }, |
| | | formItemClass: 'items-baseline', |
| | | label: 'æä»¶ä¸ä¼ ', |
| | | } |
| | | ]; |
| | | |
| | | /* |
| | | å½å被ä¸ä¼ æä»¶çæä»¶ç¶æ |
| | | true ââ æ£å¨ä¸ä¼ |
| | | false ââ ä¸ä¼ ç»æ |
| | | */ |
| | | export const UploadFileState = ref(false) |
| | | |
| | | const { apiURL, clientId } = useAppConfig( |
| | | import.meta.env, |
| | | import.meta.env.PROD, |
| | | ); |
| | | const uploadUrl = `${apiURL}/knowledge/attach/upload`; |
| | | const accessStore = useAccessStore(); |
| | | const headers = { |
| | | Authorization: `Bearer ${accessStore.accessToken}`, |
| | | clientId, |
| | | }; |
| | | |
| | | // æä»¶éå |
| | | function handleChange(res:any) { |
| | | console.log("éåäºæä»¶") |
| | | console.log(res) |
| | | } |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup lang="ts"> |
| | | import type { PropType } from 'vue'; |
| | | |
| | | import type { DeptTree } from '#/api/system/user/model'; |
| | | |
| | | import { onMounted, ref } from 'vue'; |
| | | |
| | | import { SyncOutlined } from '@ant-design/icons-vue'; |
| | | import { Empty, InputSearch, Skeleton, Tree } from 'ant-design-vue'; |
| | | |
| | | import { getDeptTree } from '#/api/system/user'; |
| | | |
| | | defineOptions({ inheritAttrs: false }); |
| | | |
| | | withDefaults(defineProps<{ showSearch?: boolean }>(), { showSearch: true }); |
| | | |
| | | const emit = defineEmits<{ |
| | | /** |
| | | * ç¹å»å·æ°æé®çäºä»¶ |
| | | */ |
| | | reload: []; |
| | | /** |
| | | * ç¹å»èç¹çäºä»¶ |
| | | */ |
| | | select: []; |
| | | }>(); |
| | | |
| | | const selectDeptId = defineModel('selectDeptId', { |
| | | required: true, |
| | | type: Array as PropType<string[]>, |
| | | }); |
| | | |
| | | const searchValue = defineModel('searchValue', { |
| | | type: String, |
| | | default: '', |
| | | }); |
| | | |
| | | /** é¨é¨æ°æ®æº */ |
| | | type DeptTreeArray = DeptTree[]; |
| | | const deptTreeArray = ref<DeptTreeArray>([]); |
| | | /** 骨æ¶å±å è½½ */ |
| | | const showTreeSkeleton = ref<boolean>(true); |
| | | |
| | | async function loadTree() { |
| | | showTreeSkeleton.value = true; |
| | | searchValue.value = ''; |
| | | selectDeptId.value = []; |
| | | |
| | | const ret = await getDeptTree(); |
| | | console.log( "ret", ret ) |
| | | deptTreeArray.value = ret; |
| | | showTreeSkeleton.value = false; |
| | | } |
| | | |
| | | async function handleReload() { |
| | | await loadTree(); |
| | | emit('reload'); |
| | | } |
| | | |
| | | onMounted(loadTree); |
| | | </script> |
| | | |
| | | <template> |
| | | <div :class="$attrs.class"> |
| | | <Skeleton |
| | | :loading="showTreeSkeleton" |
| | | :paragraph="{ rows: 8 }" |
| | | active |
| | | class="p-[8px]" |
| | | > |
| | | <div |
| | | class="bg-background flex h-full flex-col overflow-y-auto rounded-lg" |
| | | > |
| | | <!-- åºå®å¨é¡¶é¨ å¿
é¡»å ä¸bg-backgroundèæ¯è² å¦åä¼äº§ç'ç©¿é'ææ --> |
| | | <div |
| | | v-if="showSearch" |
| | | class="bg-background z-100 sticky left-0 top-0 p-[8px]" |
| | | > |
| | | <InputSearch |
| | | v-model:value="searchValue" |
| | | :placeholder="$t('pages.common.search')" |
| | | size="small" |
| | | > |
| | | <template #enterButton> |
| | | <a-button @click="handleReload"> |
| | | <SyncOutlined class="text-primary" /> |
| | | </a-button> |
| | | </template> |
| | | </InputSearch> |
| | | </div> |
| | | <div class="h-full overflow-x-hidden px-[8px]"> |
| | | <Tree |
| | | v-bind="$attrs" |
| | | v-if="deptTreeArray.length > 0" |
| | | v-model:selected-keys="selectDeptId" |
| | | :class="$attrs.class" |
| | | :field-names="{ title: 'label', key: 'id' }" |
| | | :show-line="{ showLeafIcon: false }" |
| | | :tree-data="deptTreeArray" |
| | | :virtual="false" |
| | | default-expand-all |
| | | @select="$emit('select')" |
| | | > |
| | | <template #title="{ label }"> |
| | | <span v-if="label.indexOf(searchValue) > -1"> |
| | | {{ label.substring(0, label.indexOf(searchValue)) }} |
| | | <span style="color: #f50">{{ searchValue }}</span> |
| | | {{ |
| | | label.substring( |
| | | label.indexOf(searchValue) + searchValue.length, |
| | | ) |
| | | }} |
| | | </span> |
| | | <span v-else>{{ label }}</span> |
| | | </template> |
| | | </Tree> |
| | | <!-- ä»
æ¬äººæ°æ®æé å¯ä»¥èèç´æ¥ä¸æ¾ç¤º --> |
| | | <div v-else class="mt-5"> |
| | | <Empty |
| | | :image="Empty.PRESENTED_IMAGE_SIMPLE" |
| | | description="æ é¨é¨æ°æ®" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </Skeleton> |
| | | </div> |
| | | </template> |
| | |
| | | <template> |
| | | <div>å·¥ä½ä¸å</div> |
| | | </template> |
| | | <!-- å·¥ä½å表 --> |
| | | <script setup lang="ts"> |
| | | import type { VbenFormProps } from '@vben/common-ui'; |
| | | |
| | | <script> |
| | | export default { |
| | | name: "index" |
| | | import type { VxeGridProps } from '#/adapter/vxe-table'; |
| | | import type { User } from '#/api/system/user/model'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useAccess } from '@vben/access'; |
| | | import { Page, useVbenDrawer, useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | import { preferences } from '@vben/preferences'; |
| | | import { getVxePopupContainer } from '@vben/utils'; |
| | | |
| | | import { |
| | | Avatar, |
| | | Dropdown, |
| | | Menu, |
| | | MenuItem, |
| | | Modal, |
| | | Popconfirm, |
| | | Space, |
| | | } from 'ant-design-vue'; |
| | | |
| | | import { useVbenVxeGrid, vxeCheckboxChecked } from '#/adapter/vxe-table'; |
| | | import { |
| | | userExport, |
| | | userList, |
| | | userRemove, |
| | | userStatusChange, |
| | | } from '#/api/system/user'; |
| | | import { TableSwitch } from '#/components/table'; |
| | | import { commonDownloadExcel } from '#/utils/file/download'; |
| | | |
| | | import { columns, querySchema } from './data'; |
| | | import userDrawer from './user-drawer.vue'; |
| | | import userImportModal from './user-import-modal.vue'; |
| | | import userInfoModal from './user-info-modal.vue'; |
| | | import userResetPwdModal from './user-reset-pwd-modal.vue'; |
| | | |
| | | /** |
| | | * 导å
¥ |
| | | */ |
| | | const [UserImpotModal, userImportModalApi] = useVbenModal({ |
| | | connectedComponent: userImportModal, |
| | | }); |
| | | |
| | | function handleImport() { |
| | | userImportModalApi.open(); |
| | | } |
| | | |
| | | // 左边é¨é¨ç¨ |
| | | const selectDeptId = ref<string[]>([]); |
| | | |
| | | const formOptions: VbenFormProps = { |
| | | schema: querySchema(), |
| | | commonConfig: { |
| | | labelWidth: 80, |
| | | componentProps: { |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4', |
| | | handleReset: async () => { |
| | | selectDeptId.value = []; |
| | | |
| | | const { formApi, reload } = tableApi; |
| | | await formApi.resetForm(); |
| | | const formValues = formApi.form.values; |
| | | formApi.setLatestSubmissionValues(formValues); |
| | | await reload(formValues); |
| | | }, |
| | | // æ¥æéæ©æ ¼å¼å |
| | | fieldMappingTime: [ |
| | | [ |
| | | 'createTime', |
| | | ['params[beginTime]', 'params[endTime]'], |
| | | ['YYYY-MM-DD 00:00:00', 'YYYY-MM-DD 23:59:59'], |
| | | ], |
| | | ], |
| | | }; |
| | | |
| | | const gridOptions: VxeGridProps = { |
| | | checkboxConfig: { |
| | | // é«äº® |
| | | highlight: true, |
| | | // 翻页æ¶ä¿çéä¸ç¶æ |
| | | reserve: true, |
| | | // ç¹å»è¡éä¸ |
| | | trigger: 'default', |
| | | checkMethod: ({ row }) => row?.userId !== 1, |
| | | }, |
| | | columns, |
| | | height: 'auto', |
| | | keepSource: true, |
| | | pagerConfig: {}, |
| | | proxyConfig: { |
| | | ajax: { |
| | | // è·å页颿°æ®ï¼æç´¢ï¼éç½®ä¹æ¯ |
| | | /* |
| | | @params page:页ç åæ° |
| | | @params formValues:表ååæ° |
| | | */ |
| | | query: async ({ page }, formValues = {}) => { |
| | | console.log("è·å页颿°æ®ï¼") |
| | | const res = { |
| | | rows:[ { |
| | | id:"0", //å·¥ä½idï¼ç¼å· |
| | | workName:"å·¥ä½åç§°1", //å·¥ä½åç§° |
| | | workClass:"å·¥ä½ç±»å«", //å·¥ä½ç±»å« |
| | | workContent:"å·¥ä½å
容", //å·¥ä½å
容 |
| | | projectBudget:"项ç®é¢ç®", //项ç®é¢ç® |
| | | amountProject:"项ç®éé¢", //项ç®éé¢ |
| | | responsibleDepartment:"è´è´£é¨é¨", //è´è´£é¨é¨ |
| | | Head:"è´è´£äºº", //è´è´£äºº |
| | | Annual:"年度", //年度 |
| | | assessmentTime:"èæ ¸æ¶é´",//èæ ¸æ¶é´ |
| | | assessmentIndicators:"èæ ¸ææ ", //èæ ¸ææ |
| | | File:"æä»¶å°å", //æä»¶ä¸ä¼ |
| | | taskStatus:"ä»»å¡ç¶æ", //ä»»å¡ç¶æ |
| | | assignmentStatus:"åé
ç¶æ", //åé
ç¶æ |
| | | workProgress:"å·¥ä½è¿åº¦" //å·¥ä½è¿åº¦ |
| | | } ], |
| | | total:1 |
| | | } |
| | | console.log( "res", res) |
| | | return res |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'userId', |
| | | height: 48, |
| | | }, |
| | | id: 'system-user-index', |
| | | }; |
| | | const [BasicTable, tableApi] = useVbenVxeGrid({ |
| | | formOptions, |
| | | gridOptions, |
| | | }); |
| | | |
| | | const [UserDrawer, userDrawerApi] = useVbenDrawer({ |
| | | connectedComponent: userDrawer, |
| | | }); |
| | | // å·¥ä½ä¸å |
| | | function handleAdd() { |
| | | userDrawerApi.setData({}); |
| | | userDrawerApi.open(); |
| | | } |
| | | // ç¼è¾ |
| | | function handleEdit(row: any) { |
| | | userDrawerApi.setData({ id: row.id }); |
| | | userDrawerApi.open(); |
| | | } |
| | | // æ¥ç |
| | | function handleLook(row: any) { |
| | | userDrawerApi.setData({ id: row.id, look: true }); |
| | | userDrawerApi.open(); |
| | | } |
| | | // å é¤ |
| | | async function handleDelete(row: User) { |
| | | await userRemove([row.userId]); |
| | | await tableApi.query(); |
| | | } |
| | | |
| | | function handleMultiDelete() { |
| | | const rows = tableApi.grid.getCheckboxRecords(); |
| | | const ids = rows.map((row: User) => row.userId); |
| | | Modal.confirm({ |
| | | title: 'æç¤º', |
| | | okType: 'danger', |
| | | content: `确认å é¤éä¸ç${ids.length}æ¡è®°å½åï¼`, |
| | | onOk: async () => { |
| | | await userRemove(ids); |
| | | await tableApi.query(); |
| | | }, |
| | | }); |
| | | } |
| | | |
| | | function handleDownloadExcel() { |
| | | commonDownloadExcel(userExport, 'ç¨æ·ç®¡ç', tableApi.formApi.form.values, { |
| | | fieldMappingTime: formOptions.fieldMappingTime, |
| | | }); |
| | | } |
| | | |
| | | const [UserInfoModal, userInfoModalApi] = useVbenModal({ |
| | | connectedComponent: userInfoModal, |
| | | }); |
| | | function handleUserInfo(row: User) { |
| | | userInfoModalApi.setData({ userId: row.userId }); |
| | | userInfoModalApi.open(); |
| | | } |
| | | |
| | | const [UserResetPwdModal, userResetPwdModalApi] = useVbenModal({ |
| | | connectedComponent: userResetPwdModal, |
| | | }); |
| | | |
| | | function handleResetPwd(record: User) { |
| | | userResetPwdModalApi.setData({ record }); |
| | | userResetPwdModalApi.open(); |
| | | } |
| | | |
| | | const { hasAccessByCodes } = useAccess(); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
| | | <template> |
| | | <Page :auto-content-height="true"> |
| | | <div class="flex h-full gap-[8px]"> |
| | | <BasicTable class="flex-1 overflow-hidden" table-title="å·¥ä½å表"> |
| | | <template #toolbar-tools> |
| | | <Space> |
| | | <!-- å¯¼åº --> |
| | | <!-- <a-button |
| | | @click="handleDownloadExcel" |
| | | > |
| | | {{ $t('pages.common.export') }} |
| | | </a-button> --> |
| | | <!-- 导å
¥ --> |
| | | <!-- <a-button |
| | | v-access:code="['system:user:import']" |
| | | @click="handleImport" |
| | | > |
| | | {{ $t('pages.common.import') }} |
| | | </a-button> --> |
| | | <!-- å é¤ --> |
| | | <a-button |
| | | :disabled="!vxeCheckboxChecked(tableApi)" |
| | | danger |
| | | type="primary" |
| | | @click="handleMultiDelete" |
| | | > |
| | | {{ $t('pages.common.delete') }} |
| | | </a-button> |
| | | <!-- æ°å¢ --> |
| | | <a-button |
| | | type="primary" |
| | | @click="handleAdd" |
| | | > |
| | | {{ $t('pages.common.WorkIssued') }} |
| | | </a-button> |
| | | </Space> |
| | | </template> |
| | | <template #avatar="{ row }"> |
| | | <!-- å¯è½è¦å¤æç©ºå符串æ
åµ æä»¥æ²¡æä½¿ç¨?? --> |
| | | <Avatar :src="row.avatar || preferences.app.defaultAvatar" /> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <TableSwitch |
| | | v-model="row.status" |
| | | :api="() => userStatusChange(row)" |
| | | :disabled=" |
| | | row.userId === 1 || !hasAccessByCodes(['system:user:edit']) |
| | | " |
| | | :reload="() => tableApi.query()" |
| | | /> |
| | | </template> |
| | | <template #action="{ row }"> |
| | | <Space> |
| | | <!-- æ¥ç --> |
| | | <ghost-button |
| | | @click.stop="handleLook(row)" |
| | | > |
| | | {{ $t('pages.common.look') }} |
| | | </ghost-button> |
| | | <!-- ç¼è¾ --> |
| | | <ghost-button |
| | | @click.stop="handleEdit(row)" |
| | | > |
| | | {{ $t('pages.common.edit') }} |
| | | </ghost-button> |
| | | <!-- å é¤ --> |
| | | <Popconfirm |
| | | :get-popup-container="getVxePopupContainer" |
| | | placement="left" |
| | | title="确认å é¤ï¼" |
| | | @confirm="handleDelete(row)" |
| | | > |
| | | <ghost-button |
| | | danger |
| | | @click.stop="" |
| | | > |
| | | {{ $t('pages.common.delete') }} |
| | | </ghost-button> |
| | | </Popconfirm> |
| | | </Space> |
| | | </template> |
| | | </BasicTable> |
| | | </div> |
| | | <UserImpotModal @reload="tableApi.query()" /> |
| | | <UserDrawer @reload="tableApi.query()" /> |
| | | <UserInfoModal /> |
| | | <UserResetPwdModal /> |
| | | </Page> |
| | | </template> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { DescItem } from '#/components/description'; |
| | | |
| | | import { DictEnum } from '@vben/constants'; |
| | | |
| | | import { Tag } from 'ant-design-vue'; |
| | | import dayjs from 'dayjs'; |
| | | import duration from 'dayjs/plugin/duration'; |
| | | import relativeTime from 'dayjs/plugin/relativeTime'; |
| | | |
| | | import { renderDict } from '#/utils/render'; |
| | | |
| | | dayjs.extend(duration); |
| | | dayjs.extend(relativeTime); |
| | | |
| | | function renderTags(list: string[]) { |
| | | return ( |
| | | <div class="flex flex-row flex-wrap gap-0.5"> |
| | | {list.map((item) => ( |
| | | <Tag key={item}>{item}</Tag> |
| | | ))} |
| | | </div> |
| | | ); |
| | | } |
| | | |
| | | export const descSchema: DescItem[] = [ |
| | | { |
| | | field: 'userId', |
| | | label: 'ç¨æ·ID', |
| | | }, |
| | | { |
| | | field: 'status', |
| | | label: 'ç¨æ·ç¶æ', |
| | | render(value) { |
| | | return renderDict(value, DictEnum.SYS_NORMAL_DISABLE); |
| | | }, |
| | | }, |
| | | { |
| | | field: 'nickName', |
| | | label: 'ç¨æ·ä¿¡æ¯', |
| | | render(_, data) { |
| | | const { deptName = 'ææ é¨é¨ä¿¡æ¯', nickName, userName } = data; |
| | | // 为äºå
¼å®¹æ°çæ¬åæ§çæ¬ |
| | | let currentDept = deptName; |
| | | if (data.dept && data.dept.deptName) { |
| | | currentDept = data.dept.deptName; |
| | | } |
| | | return `${userName} / ${nickName} / ${currentDept}`; |
| | | }, |
| | | }, |
| | | { |
| | | field: 'phonenumber', |
| | | label: 'ææºå·', |
| | | render(value) { |
| | | return value || 'æªè®¾ç½®ææºå·ç '; |
| | | }, |
| | | }, |
| | | { |
| | | field: 'email', |
| | | label: 'é®ç®±', |
| | | render(value) { |
| | | return value || 'æªè®¾ç½®é®ç®±å°å'; |
| | | }, |
| | | }, |
| | | { |
| | | field: 'postNames', |
| | | label: 'å²ä½', |
| | | render(value) { |
| | | if (Array.isArray(value) && value.length === 0) { |
| | | return 'ææ ä¿¡æ¯'; |
| | | } |
| | | return renderTags(value); |
| | | }, |
| | | }, |
| | | { |
| | | field: 'roleNames', |
| | | label: 'æé', |
| | | render(value) { |
| | | if (Array.isArray(value) && value.length === 0) { |
| | | return 'ææ ä¿¡æ¯'; |
| | | } |
| | | return renderTags(value); |
| | | }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | label: 'å建æ¶é´', |
| | | }, |
| | | { |
| | | field: 'loginIp', |
| | | label: '䏿¬¡ç»å½IP', |
| | | render(value) { |
| | | return value || <span class="text-orange-500">仿ªç»å½è¿</span>; |
| | | }, |
| | | }, |
| | | { |
| | | field: 'loginDate', |
| | | label: '䏿¬¡ç»å½æ¶é´', |
| | | render(value) { |
| | | if (!value) { |
| | | return <span class="text-orange-500">仿ªç»å½è¿</span>; |
| | | } |
| | | // é»è®¤enæ¾ç¤º |
| | | dayjs.locale('zh-cn'); |
| | | // 计ç®ç¸å·®ç§æ° |
| | | const diffSeconds = dayjs().diff(dayjs(value), 'second'); |
| | | /** |
| | | * 转为æ¶é´æ¾ç¤º(xæ x天) |
| | | * https://dayjs.fenxianglu.cn/category/duration.html#%E4%BA%BA%E6%80%A7%E5%8C%96 |
| | | * |
| | | */ |
| | | const diffText = dayjs.duration(diffSeconds, 'seconds').humanize(); |
| | | return ( |
| | | <div class="flex gap-2"> |
| | | {value} |
| | | <Tag bordered={false} color="cyan"> |
| | | {diffText}å |
| | | </Tag> |
| | | </div> |
| | | ); |
| | | }, |
| | | }, |
| | | { |
| | | field: 'remark', |
| | | label: '夿³¨', |
| | | render(value) { |
| | | return value || 'æ '; |
| | | }, |
| | | }, |
| | | ]; |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup lang="ts"> |
| | | import type { Role } from '#/api/system/user/model'; |
| | | |
| | | import { computed, h, ref } from 'vue'; |
| | | |
| | | import { useVbenDrawer } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | import { cloneDeep } from '@vben/utils'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { findUserInfo, userAdd, userUpdate } from '#/api/system/user'; |
| | | import { authScopeOptions } from '#/views/system/role/data'; |
| | | |
| | | import { drawerSchema, UploadFileState } from './data'; |
| | | import { deptList } from '#/api/system/dept/index'; |
| | | |
| | | const emit = defineEmits<{ reload: [] }>(); |
| | | |
| | | const isUpdate = ref(false); //å½åæ¯å¦ä¸ºç¼è¾ |
| | | const isLook = ref(false); //å½åæ¯å¦ä¸ºæ¥ç |
| | | const title = computed(() => { |
| | | let text = ''; |
| | | if (isLook.value) { |
| | | text = $t('pages.common.look'); |
| | | } else if (isUpdate.value) { |
| | | text = $t('pages.common.edit'); |
| | | } else if (!isUpdate.value) { |
| | | text = $t('pages.common.add'); |
| | | } |
| | | return text; |
| | | }); |
| | | |
| | | const [BasicForm, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 80, |
| | | }, |
| | | schema: drawerSchema(), |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-2', |
| | | }); |
| | | |
| | | /** |
| | | * çæè§è²çèªå®ä¹label |
| | | * ä¹å¯ä»¥ç¨optionææ§½æ¥å |
| | | * renderComponentContent: () => ({ |
| | | option: ({value, label, [disabled, key, title]}) => '', |
| | | }), |
| | | */ |
| | | function genRoleOptionlabel(role: Role) { |
| | | const found = authScopeOptions.find((item) => item.value === role.dataScope); |
| | | if (!found) { |
| | | return role.roleName; |
| | | } |
| | | return h('div', { class: 'flex items-center gap-[6px]' }, [ |
| | | h('span', null, role.roleName), |
| | | h(Tag, { color: found.color }, () => found.label), |
| | | ]); |
| | | } |
| | | // æå¼ä»¥åç¼è¾é½ä¼è°ç¨è¯¥äºä»¶ |
| | | const [BasicDrawer, drawerApi] = useVbenDrawer({ |
| | | onCancel: handleCancel, |
| | | onConfirm: handleConfirm, |
| | | async onOpenChange(isOpen) { |
| | | if (isOpen) { |
| | | // åå§åä¸ä¸è¡¨åé项 |
| | | const deptlist = await ( |
| | | await deptList() |
| | | ).map((item) => { |
| | | return { |
| | | ...item, |
| | | value: item.deptId, |
| | | label: item.deptName, |
| | | }; |
| | | }); |
| | | formApi.updateSchema([ |
| | | { |
| | | componentProps: { options: deptlist }, |
| | | fieldName: 'responsibleDepartment', |
| | | }, |
| | | ]); |
| | | } |
| | | |
| | | if (!isOpen) { |
| | | // éè¦éç½®å²ä½éæ© |
| | | formApi.updateSchema([ |
| | | { |
| | | componentProps: { options: [], placeholder: '请å
éæ©é¨é¨' }, |
| | | fieldName: 'postIds', |
| | | }, |
| | | ]); |
| | | return null; |
| | | } |
| | | drawerApi.drawerLoading(true); |
| | | const { id } = drawerApi.getData() as { id?: number | string }; |
| | | const { look } = drawerApi.getData() as { look?: boolean }; |
| | | isUpdate.value = !!id; |
| | | isLook.value = !!look; |
| | | /** updateæ¶ ç¦ç¨ç¨æ·åä¿®æ¹ ä¸æ¾ç¤ºå¯ç æ¡ */ |
| | | // å¦æä¸ºæ¥ç忿项é½ä¸å¯ç¼è¾ |
| | | formApi.updateSchema([ |
| | | { componentProps: { disabled: isLook.value }, fieldName: 'workName' }, |
| | | { componentProps: { disabled: isLook.value }, fieldName: 'workClass' }, |
| | | { componentProps: { disabled: isLook.value }, fieldName: 'workContent' }, |
| | | { |
| | | componentProps: { disabled: isLook.value }, |
| | | fieldName: 'projectBudget', |
| | | }, |
| | | { |
| | | componentProps: { disabled: isLook.value }, |
| | | fieldName: 'amountProject', |
| | | }, |
| | | { |
| | | componentProps: { disabled: isLook.value }, |
| | | fieldName: 'responsibleDepartment', |
| | | }, |
| | | { componentProps: { disabled: isLook.value }, fieldName: 'Head' }, |
| | | { componentProps: { disabled: isLook.value }, fieldName: 'Annual' }, |
| | | { |
| | | componentProps: { disabled: isLook.value }, |
| | | fieldName: 'assessmentTime', |
| | | }, |
| | | { |
| | | componentProps: { disabled: isLook.value }, |
| | | fieldName: 'assessmentIndicators', |
| | | }, |
| | | { componentProps: { disabled: isLook.value }, fieldName: 'File' }, |
| | | ]); |
| | | |
| | | drawerApi.setState({ |
| | | showConfirmButton: !isLook.value, |
| | | }); |
| | | |
| | | // æ´æ° && èµå¼ |
| | | // const { postIds, posts, roleIds, roles, user } = await findUserInfo(id); //è°ç¨æ¥å£è·å详ç»ä¿¡æ¯ |
| | | |
| | | const data = { |
| | | id: '0', //å·¥ä½idï¼ç¼å· |
| | | workName: 'å·¥ä½åç§°1', //å·¥ä½åç§° |
| | | workClass: 'å·¥ä½ç±»å«', //å·¥ä½ç±»å« |
| | | workContent: 'å·¥ä½å
容', //å·¥ä½å
容 |
| | | projectBudget: '项ç®é¢ç®', //项ç®é¢ç® |
| | | amountProject: '项ç®éé¢', //项ç®éé¢ |
| | | responsibleDepartment: 'è´è´£é¨é¨', //è´è´£é¨é¨ |
| | | Head: 'è´è´£äºº', //è´è´£äºº |
| | | Annual: '2025', //年度 |
| | | assessmentTime: '2025-06-25', //èæ ¸æ¶é´ |
| | | assessmentIndicators: 'èæ ¸ææ ', //èæ ¸ææ |
| | | File: 'æä»¶å°å', //æä»¶ä¸ä¼ |
| | | taskStatus: 'ä»»å¡ç¶æ', //ä»»å¡ç¶æ |
| | | assignmentStatus: 'åé
ç¶æ', //åé
ç¶æ |
| | | workProgress: 'å·¥ä½è¿åº¦', //å·¥ä½è¿åº¦ |
| | | }; |
| | | |
| | | if (data && id) { |
| | | await Promise.all([ |
| | | // æ·»å åºç¡ä¿¡æ¯ |
| | | formApi.setValues(data), |
| | | ]); |
| | | } |
| | | drawerApi.drawerLoading(false); |
| | | console.log(isLook.value); |
| | | }, |
| | | }); |
| | | // 表åæäº¤ï¼ç¼è¾ä¸æ°å¢é½ä¼è°ç¨è¿ä¸ªæ¹æ³ä½ |
| | | async function handleConfirm() { |
| | | if( UploadFileState.value ){ |
| | | message.warn("å½åææä»¶æ£å¨ä¸ä¼ ï¼è¯·èå¿çå¾
") |
| | | return |
| | | } |
| | | try { |
| | | drawerApi.drawerLoading(true); |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | const data = cloneDeep(await formApi.getValues()); //表åå
çæ°æ® |
| | | console.log(data) |
| | | // await (isUpdate.value ? userUpdate(data) : userAdd(data)); |
| | | emit('reload'); |
| | | await handleCancel(); |
| | | } catch (error) { |
| | | console.error(error); |
| | | } finally { |
| | | drawerApi.drawerLoading(false); |
| | | } |
| | | } |
| | | |
| | | async function handleCancel() { |
| | | drawerApi.close(); |
| | | await formApi.resetForm(); |
| | | } |
| | | |
| | | function IsShowConfirmButton(params: type) { |
| | | if (isLook.value) { |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | // å¨è¿å è½½äºæ°æ® |
| | | </script> |
| | | |
| | | <template> |
| | | <BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]"> |
| | | <BasicForm /> |
| | | </BasicDrawer> |
| | | </template> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup lang="ts"> |
| | | import type { UploadFile } from 'ant-design-vue/es/upload/interface'; |
| | | |
| | | import { h, ref, unref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { ExcelIcon, InBoxIcon } from '@vben/icons'; |
| | | |
| | | import { Modal, Switch, Upload } from 'ant-design-vue'; |
| | | |
| | | import { downloadImportTemplate, userImportData } from '#/api/system/user'; |
| | | import { commonDownloadExcel } from '#/utils/file/download'; |
| | | |
| | | const emit = defineEmits<{ reload: [] }>(); |
| | | |
| | | const UploadDragger = Upload.Dragger; |
| | | |
| | | const [BasicModal, modalApi] = useVbenModal({ |
| | | onCancel: handleCancel, |
| | | onConfirm: handleSubmit, |
| | | }); |
| | | |
| | | const fileList = ref<UploadFile[]>([]); |
| | | const checked = ref(false); |
| | | |
| | | async function handleSubmit() { |
| | | try { |
| | | modalApi.modalLoading(true); |
| | | if (fileList.value.length !== 1) { |
| | | handleCancel(); |
| | | return; |
| | | } |
| | | const data = { |
| | | file: fileList.value[0]!.originFileObj as Blob, |
| | | updateSupport: unref(checked), |
| | | }; |
| | | const { code, msg } = await userImportData(data); |
| | | let modal = Modal.success; |
| | | if (code === 200) { |
| | | emit('reload'); |
| | | } else { |
| | | modal = Modal.error; |
| | | } |
| | | handleCancel(); |
| | | modal({ |
| | | content: h('div', { |
| | | class: 'max-h-[260px] overflow-y-auto', |
| | | innerHTML: msg, // åå°å·²ç»å¤çxssé®é¢ |
| | | }), |
| | | title: 'æç¤º', |
| | | }); |
| | | } catch (error) { |
| | | console.warn(error); |
| | | modalApi.close(); |
| | | } finally { |
| | | modalApi.modalLoading(false); |
| | | } |
| | | } |
| | | |
| | | function handleCancel() { |
| | | modalApi.close(); |
| | | fileList.value = []; |
| | | checked.value = false; |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <BasicModal |
| | | :close-on-click-modal="false" |
| | | :fullscreen-button="false" |
| | | title="ç¨æ·å¯¼å
¥" |
| | | > |
| | | <!-- z-indexä¸è®¾ç½®ä¼é®æ¡æ¨¡æ¿ä¸è½½loading --> |
| | | <!-- æå¨å¤ç è䏿¯æ¾å
¥æä»¶å°±ä¸ä¼ --> |
| | | <UploadDragger |
| | | v-model:file-list="fileList" |
| | | :before-upload="() => false" |
| | | :max-count="1" |
| | | :show-upload-list="true" |
| | | accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" |
| | | > |
| | | <p class="ant-upload-drag-icon flex items-center justify-center"> |
| | | <InBoxIcon class="text-primary size-[48px]" /> |
| | | </p> |
| | | <p class="ant-upload-text">ç¹å»æè
ææ½å°æ¤å¤ä¸ä¼ æä»¶</p> |
| | | </UploadDragger> |
| | | <div class="mt-2 flex flex-col gap-2"> |
| | | <div class="flex items-center gap-2"> |
| | | <span>å
许导å
¥xlsx, xlsæä»¶</span> |
| | | <a-button |
| | | type="link" |
| | | @click="commonDownloadExcel(downloadImportTemplate, 'ç¨æ·å¯¼å
¥æ¨¡æ¿')" |
| | | > |
| | | <div class="flex items-center gap-[4px]"> |
| | | <ExcelIcon /> |
| | | <span>ä¸è½½æ¨¡æ¿</span> |
| | | </div> |
| | | </a-button> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span :class="{ 'text-red-500': checked }"> |
| | | æ¯å¦æ´æ°/è¦çå·²åå¨çç¨æ·æ°æ® |
| | | </span> |
| | | <Switch v-model:checked="checked" /> |
| | | </div> |
| | | </div> |
| | | </BasicModal> |
| | | </template> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup lang="ts"> |
| | | import type { User } from '#/api/system/user/model'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { findUserInfo } from '#/api/system/user'; |
| | | import { Description, useDescription } from '#/components/description'; |
| | | |
| | | import { descSchema } from './info'; |
| | | |
| | | const [BasicModal, modalApi] = useVbenModal({ |
| | | onOpenChange: handleOpenChange, |
| | | }); |
| | | |
| | | const [registerDescription, { setDescProps }] = useDescription({ |
| | | column: 1, |
| | | labelStyle: { |
| | | minWidth: '150px', |
| | | width: '150px', |
| | | }, |
| | | schema: descSchema, |
| | | }); |
| | | |
| | | async function handleOpenChange(open: boolean) { |
| | | if (!open) { |
| | | return null; |
| | | } |
| | | modalApi.modalLoading(true); |
| | | |
| | | const { userId } = modalApi.getData() as { userId: number | string }; |
| | | const response = await findUserInfo(userId); |
| | | // å¤é¨çroleIds postIdsææ¯çæ£å¯¹åºç æ°å¢æ¶ä¸ºç©º |
| | | // postsæä¸ºNullçæ
åµ éè¦ç»é»è®¤å¼ |
| | | const { postIds = [], posts = [], roleIds = [], roles = [], user } = response; |
| | | |
| | | const postNames = posts |
| | | .filter((item) => postIds.includes(item.postId)) |
| | | .map((item) => item.postName); |
| | | |
| | | const roleNames = roles |
| | | .filter((item) => roleIds.includes(item.roleId)) |
| | | .map((item) => item.roleName); |
| | | |
| | | interface UserWithNames extends User { |
| | | postNames: string[]; |
| | | roleNames: string[]; |
| | | } |
| | | (user as UserWithNames).postNames = postNames; |
| | | (user as UserWithNames).roleNames = roleNames; |
| | | |
| | | // èµå¼ |
| | | setDescProps({ data: user }); |
| | | |
| | | modalApi.modalLoading(false); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <BasicModal :footer="false" :fullscreen-button="false" title="ç¨æ·ä¿¡æ¯"> |
| | | <Description @register="registerDescription" /> |
| | | </BasicModal> |
| | | </template> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup lang="ts"> |
| | | import type { ResetPwdParam, User } from '#/api/system/user/model'; |
| | | |
| | | import { useVbenModal, z } from '@vben/common-ui'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { userResetPassword } from '#/api/system/user'; |
| | | import { Description, useDescription } from '#/components/description'; |
| | | |
| | | const emit = defineEmits<{ reload: [] }>(); |
| | | |
| | | const [BasicModal, modalApi] = useVbenModal({ |
| | | onCancel: handleCancel, |
| | | onConfirm: handleSubmit, |
| | | onOpenChange: handleOpenChange, |
| | | }); |
| | | |
| | | const [registerDescription, { setDescProps }] = useDescription({ |
| | | column: 1, |
| | | schema: [ |
| | | { |
| | | field: 'userId', |
| | | label: 'ç¨æ·ID', |
| | | }, |
| | | { |
| | | field: 'userName', |
| | | label: 'ç¨æ·å', |
| | | }, |
| | | { |
| | | field: 'nickName', |
| | | label: 'æµç§°', |
| | | }, |
| | | ], |
| | | }); |
| | | |
| | | const [BasicForm, formApi] = useVbenForm({ |
| | | schema: [ |
| | | { |
| | | component: 'Input', |
| | | dependencies: { |
| | | show: () => false, |
| | | triggerFields: [''], |
| | | }, |
| | | fieldName: 'userId', |
| | | label: 'ç¨æ·ID', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'InputPassword', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ°çå¯ç , å¯ç é¿åº¦ä¸º5 - 20', |
| | | }, |
| | | fieldName: 'password', |
| | | label: 'æ°çå¯ç ', |
| | | rules: z |
| | | .string() |
| | | .min(5, { message: 'å¯ç é¿åº¦ä¸º5 - 20' }) |
| | | .max(20, { message: 'å¯ç é¿åº¦ä¸º5 - 20' }), |
| | | }, |
| | | ], |
| | | showDefaultActions: false, |
| | | commonConfig: { |
| | | labelWidth: 80, |
| | | }, |
| | | }); |
| | | |
| | | async function handleOpenChange(open: boolean) { |
| | | if (!open) { |
| | | return null; |
| | | } |
| | | const { record } = modalApi.getData() as { record: User }; |
| | | setDescProps({ data: record }, true); |
| | | await formApi.setValues({ userId: record.userId }); |
| | | } |
| | | |
| | | async function handleSubmit() { |
| | | try { |
| | | modalApi.modalLoading(true); |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | const data = await formApi.getValues(); |
| | | await userResetPassword(data as ResetPwdParam); |
| | | emit('reload'); |
| | | handleCancel(); |
| | | } catch (error) { |
| | | console.error(error); |
| | | } finally { |
| | | modalApi.modalLoading(false); |
| | | } |
| | | } |
| | | |
| | | async function handleCancel() { |
| | | modalApi.close(); |
| | | await formApi.resetForm(); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <BasicModal |
| | | :close-on-click-modal="false" |
| | | :fullscreen-button="false" |
| | | title="éç½®å¯ç " |
| | | > |
| | | <div class="flex flex-col gap-[12px]"> |
| | | <Description @register="registerDescription" /> |
| | | <BasicForm /> |
| | | </div> |
| | | </BasicModal> |
| | | </template> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { FormSchemaGetter } from '#/adapter/form'; |
| | | import type { VxeGridProps } from '#/adapter/vxe-table'; |
| | | |
| | | import { DictEnum } from '@vben/constants'; |
| | | import { getPopupContainer } from '@vben/utils'; |
| | | import { useAppConfig } from '@vben/hooks'; |
| | | import { useAccessStore } from '@vben/stores'; |
| | | import type { UploadFile } from 'ant-design-vue'; |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | import { getDictOptions } from '#/utils/dict'; |
| | | import { Item } from 'ant-design-vue/es/menu'; |
| | | // import { OptionsItem } from "" |
| | | |
| | | export const querySchema: FormSchemaGetter = () => [ |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'workName', |
| | | label: 'å·¥ä½åç§°', |
| | | }, |
| | | { |
| | | component: 'Select', |
| | | componentProps: { |
| | | getPopupContainer, |
| | | options: getDictOptions(DictEnum.TASK_STATUS), |
| | | }, |
| | | fieldName: 'workClass', |
| | | label: 'å·¥ä½ç±»å«', |
| | | }, |
| | | { |
| | | component: 'Select', |
| | | componentProps: { |
| | | getPopupContainer, |
| | | options: getDictOptions(DictEnum.TASK_STATUS), |
| | | }, |
| | | fieldName: 'taskStatus', |
| | | label: 'ä»»å¡ç¶æ', |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'Annual', |
| | | label: '年度', |
| | | }, |
| | | ]; |
| | | |
| | | export const columns: VxeGridProps['columns'] = [ |
| | | { type: 'checkbox', width: 60 }, |
| | | { |
| | | field: 'workName', |
| | | title: 'åç§°', |
| | | }, |
| | | { |
| | | field: 'id', |
| | | title: 'ç¼å·', |
| | | }, |
| | | { |
| | | field: 'workClass', |
| | | title: 'ç±»å«', |
| | | }, |
| | | { |
| | | field: 'assessmentTime', |
| | | title: 'èæ ¸æ¶é´', |
| | | }, |
| | | { |
| | | field: 'taskStatus', |
| | | title: 'ä»»å¡ç¶æ', |
| | | }, |
| | | { |
| | | field: 'Annual', |
| | | title: '年度', |
| | | }, |
| | | { |
| | | field: 'workProgress', |
| | | title: 'å·¥ä½è¿åº¦', |
| | | }, |
| | | { |
| | | field: 'action', |
| | | fixed: 'right', |
| | | slots: { default: 'action' }, |
| | | title: 'æä½', |
| | | width: 180, |
| | | }, |
| | | ]; |
| | | // å·¥ä½è¯¦æ
|
| | | export const drawerSchema: FormSchemaGetter = () => [ |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'workName', |
| | | label: 'å·¥ä½åç§°', |
| | | rules: 'required' |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'workClass', |
| | | label: 'å·¥ä½ç±»å«', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'workContent', |
| | | label: 'å·¥ä½å
容' |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'projectBudget', |
| | | label: '项ç®é¢ç®%', |
| | | defaultValue: undefined |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'amountProject', |
| | | defaultValue: undefined, |
| | | label: '项ç®éé¢' |
| | | }, |
| | | { |
| | | component: 'Select', |
| | | componentProps: { |
| | | getPopupContainer, |
| | | options: [] |
| | | }, |
| | | fieldName: 'responsibleDepartment', |
| | | label: 'è´è´£é¨é¨', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'Select', |
| | | componentProps: { |
| | | getPopupContainer, |
| | | options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE) |
| | | }, |
| | | fieldName: 'Head', |
| | | label: 'è´è´£äºº', |
| | | }, |
| | | { |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | format: 'YYYY', |
| | | showTime: true, |
| | | valueFormat: 'YYYY', |
| | | picker: "year", |
| | | getPopupContainer, |
| | | }, |
| | | fieldName: 'Annual', |
| | | label: '年度', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | format: 'YYYY-MM-DD', |
| | | showTime: true, |
| | | valueFormat: 'YYYY-MM-DD', |
| | | getPopupContainer, |
| | | }, |
| | | fieldName: 'assessmentTime', |
| | | label: 'èæ ¸æ¶é´', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'Select', |
| | | fieldName: 'assessmentIndicators', |
| | | formItemClass: 'items-baseline', |
| | | label: 'èæ ¸ææ ', |
| | | }, |
| | | { |
| | | fieldName: 'File', |
| | | component: 'NewFileUpload', |
| | | componentProps: { |
| | | action: uploadUrl, |
| | | headers: headers, |
| | | maxSize: 20, |
| | | onDelete:(file: UploadFile)=>{ |
| | | console.log("åå¨æä»¶è¢«å é¤",file) |
| | | } |
| | | }, |
| | | formItemClass: 'items-baseline', |
| | | label: 'æä»¶ä¸ä¼ ', |
| | | } |
| | | ]; |
| | | |
| | | // å·¥ä½ä¸å |
| | | export const AssigningWorkSchema: FormSchemaGetter = () => [ |
| | | { |
| | | component: 'Select', |
| | | fieldName: 'assessmentIndicators', |
| | | formItemClass: 'items-baseline', |
| | | label: 'è´è´£äºº', |
| | | }, |
| | | ] |
| | | |
| | | // 工使±æ¥ |
| | | export const ReportScheme: FormSchemaGetter = () => [ |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'workName', |
| | | label: 'å·¥ä½åç§°', |
| | | rules: 'required' |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'workClass', |
| | | label: 'å·¥ä½ç±»å«', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'Textarea', |
| | | fieldName: 'workContent', |
| | | label: 'å·¥ä½å
容' |
| | | }, |
| | | { |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | format: 'YYYY-MM-DD', |
| | | showTime: true, |
| | | valueFormat: 'YYYY-MM-DD', |
| | | getPopupContainer, |
| | | }, |
| | | fieldName: 'DebriefingTime', |
| | | label: 'æ±æ¥æ¶é´', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'workProgress', |
| | | label: '宿è¿åº¦' |
| | | }, |
| | | { |
| | | fieldName: 'File', |
| | | component: 'NewFileUpload', |
| | | componentProps: { |
| | | action: uploadUrl, |
| | | headers: headers, |
| | | maxSize: 20, |
| | | onDelete:(file: UploadFile)=>{ |
| | | console.log("åå¨æä»¶è¢«å é¤",file) |
| | | } |
| | | }, |
| | | formItemClass: 'items-baseline', |
| | | label: 'æä»¶ä¸ä¼ ', |
| | | }, |
| | | { |
| | | component: 'Input', |
| | | fieldName: 'workProgress', |
| | | label: 'èµé使ç¨(ä¸å
)' |
| | | }, |
| | | { |
| | | component: 'Textarea', |
| | | fieldName: 'remark', |
| | | label: '夿³¨' |
| | | }, |
| | | ] |
| | | |
| | | const { apiURL, clientId } = useAppConfig( |
| | | import.meta.env, |
| | | import.meta.env.PROD, |
| | | ); |
| | | const uploadUrl = `${apiURL}/knowledge/attach/upload`; |
| | | const accessStore = useAccessStore(); |
| | | const headers = { |
| | | Authorization: `Bearer ${accessStore.accessToken}`, |
| | | clientId, |
| | | }; |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup lang="ts"> |
| | | import type { PropType } from 'vue'; |
| | | |
| | | import type { DeptTree } from '#/api/system/user/model'; |
| | | |
| | | import { onMounted, ref } from 'vue'; |
| | | |
| | | import { SyncOutlined } from '@ant-design/icons-vue'; |
| | | import { Empty, InputSearch, Skeleton, Tree } from 'ant-design-vue'; |
| | | |
| | | import { getDeptTree } from '#/api/system/user'; |
| | | |
| | | defineOptions({ inheritAttrs: false }); |
| | | |
| | | withDefaults(defineProps<{ showSearch?: boolean }>(), { showSearch: true }); |
| | | |
| | | const emit = defineEmits<{ |
| | | /** |
| | | * ç¹å»å·æ°æé®çäºä»¶ |
| | | */ |
| | | reload: []; |
| | | /** |
| | | * ç¹å»èç¹çäºä»¶ |
| | | */ |
| | | select: []; |
| | | }>(); |
| | | |
| | | const selectDeptId = defineModel('selectDeptId', { |
| | | required: true, |
| | | type: Array as PropType<string[]>, |
| | | }); |
| | | |
| | | const searchValue = defineModel('searchValue', { |
| | | type: String, |
| | | default: '', |
| | | }); |
| | | |
| | | /** é¨é¨æ°æ®æº */ |
| | | type DeptTreeArray = DeptTree[]; |
| | | const deptTreeArray = ref<DeptTreeArray>([]); |
| | | /** 骨æ¶å±å è½½ */ |
| | | const showTreeSkeleton = ref<boolean>(true); |
| | | |
| | | async function loadTree() { |
| | | showTreeSkeleton.value = true; |
| | | searchValue.value = ''; |
| | | selectDeptId.value = []; |
| | | |
| | | const ret = await getDeptTree(); |
| | | console.log( "ret", ret ) |
| | | deptTreeArray.value = ret; |
| | | showTreeSkeleton.value = false; |
| | | } |
| | | |
| | | async function handleReload() { |
| | | await loadTree(); |
| | | emit('reload'); |
| | | } |
| | | |
| | | onMounted(loadTree); |
| | | </script> |
| | | |
| | | <template> |
| | | <div :class="$attrs.class"> |
| | | <Skeleton |
| | | :loading="showTreeSkeleton" |
| | | :paragraph="{ rows: 8 }" |
| | | active |
| | | class="p-[8px]" |
| | | > |
| | | <div |
| | | class="bg-background flex h-full flex-col overflow-y-auto rounded-lg" |
| | | > |
| | | <!-- åºå®å¨é¡¶é¨ å¿
é¡»å ä¸bg-backgroundèæ¯è² å¦åä¼äº§ç'ç©¿é'ææ --> |
| | | <div |
| | | v-if="showSearch" |
| | | class="bg-background z-100 sticky left-0 top-0 p-[8px]" |
| | | > |
| | | <InputSearch |
| | | v-model:value="searchValue" |
| | | :placeholder="$t('pages.common.search')" |
| | | size="small" |
| | | > |
| | | <template #enterButton> |
| | | <a-button @click="handleReload"> |
| | | <SyncOutlined class="text-primary" /> |
| | | </a-button> |
| | | </template> |
| | | </InputSearch> |
| | | </div> |
| | | <div class="h-full overflow-x-hidden px-[8px]"> |
| | | <Tree |
| | | v-bind="$attrs" |
| | | v-if="deptTreeArray.length > 0" |
| | | v-model:selected-keys="selectDeptId" |
| | | :class="$attrs.class" |
| | | :field-names="{ title: 'label', key: 'id' }" |
| | | :show-line="{ showLeafIcon: false }" |
| | | :tree-data="deptTreeArray" |
| | | :virtual="false" |
| | | default-expand-all |
| | | @select="$emit('select')" |
| | | > |
| | | <template #title="{ label }"> |
| | | <span v-if="label.indexOf(searchValue) > -1"> |
| | | {{ label.substring(0, label.indexOf(searchValue)) }} |
| | | <span style="color: #f50">{{ searchValue }}</span> |
| | | {{ |
| | | label.substring( |
| | | label.indexOf(searchValue) + searchValue.length, |
| | | ) |
| | | }} |
| | | </span> |
| | | <span v-else>{{ label }}</span> |
| | | </template> |
| | | </Tree> |
| | | <!-- ä»
æ¬äººæ°æ®æé å¯ä»¥èèç´æ¥ä¸æ¾ç¤º --> |
| | | <div v-else class="mt-5"> |
| | | <Empty |
| | | :image="Empty.PRESENTED_IMAGE_SIMPLE" |
| | | description="æ é¨é¨æ°æ®" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </Skeleton> |
| | | </div> |
| | | </template> |
| | |
| | | <template> |
| | | <div>æçå·¥ä½</div> |
| | | </template> |
| | | <!-- æçå·¥ä½ --> |
| | | <script setup lang="ts"> |
| | | import type { VbenFormProps } from '@vben/common-ui'; |
| | | |
| | | <script> |
| | | export default { |
| | | name: "index" |
| | | import type { VxeGridProps } from '#/adapter/vxe-table'; |
| | | import type { User } from '#/api/system/user/model'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useAccess } from '@vben/access'; |
| | | import { Page, useVbenDrawer, useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | import { preferences } from '@vben/preferences'; |
| | | import { getVxePopupContainer } from '@vben/utils'; |
| | | |
| | | import { |
| | | Avatar, |
| | | Dropdown, |
| | | Menu, |
| | | MenuItem, |
| | | Modal, |
| | | Popconfirm, |
| | | Space, |
| | | } from 'ant-design-vue'; |
| | | |
| | | import { useVbenVxeGrid, vxeCheckboxChecked } from '#/adapter/vxe-table'; |
| | | import { |
| | | userExport, |
| | | userList, |
| | | userRemove, |
| | | userStatusChange, |
| | | } from '#/api/system/user'; |
| | | import { TableSwitch } from '#/components/table'; |
| | | import { commonDownloadExcel } from '#/utils/file/download'; |
| | | |
| | | import { columns, querySchema } from './data'; |
| | | import userDrawer from './user-drawer.vue'; //å·¥ä½è¯¦æ
|
| | | import userAssignWork from './user-assign-work.vue'; //å·¥ä½ä¸å |
| | | import userReport from './user-report.vue'; //工使±æ¥ |
| | | import userImportModal from './user-import-modal.vue'; |
| | | import userInfoModal from './user-info-modal.vue'; |
| | | import userResetPwdModal from './user-reset-pwd-modal.vue'; |
| | | |
| | | /** |
| | | * 导å
¥ |
| | | */ |
| | | const [UserImpotModal, userImportModalApi] = useVbenModal({ |
| | | connectedComponent: userImportModal, |
| | | }); |
| | | |
| | | function handleImport() { |
| | | userImportModalApi.open(); |
| | | } |
| | | |
| | | // 左边é¨é¨ç¨ |
| | | const selectDeptId = ref<string[]>([]); |
| | | |
| | | const formOptions: VbenFormProps = { |
| | | schema: querySchema(), |
| | | commonConfig: { |
| | | labelWidth: 80, |
| | | componentProps: { |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4', |
| | | handleReset: async () => { |
| | | selectDeptId.value = []; |
| | | |
| | | const { formApi, reload } = tableApi; |
| | | await formApi.resetForm(); |
| | | const formValues = formApi.form.values; |
| | | formApi.setLatestSubmissionValues(formValues); |
| | | await reload(formValues); |
| | | }, |
| | | // æ¥æéæ©æ ¼å¼å |
| | | fieldMappingTime: [ |
| | | [ |
| | | 'createTime', |
| | | ['params[beginTime]', 'params[endTime]'], |
| | | ['YYYY-MM-DD 00:00:00', 'YYYY-MM-DD 23:59:59'], |
| | | ], |
| | | ], |
| | | }; |
| | | |
| | | const gridOptions: VxeGridProps = { |
| | | checkboxConfig: { |
| | | // é«äº® |
| | | highlight: true, |
| | | // 翻页æ¶ä¿çéä¸ç¶æ |
| | | reserve: true, |
| | | // ç¹å»è¡éä¸ |
| | | trigger: 'default', |
| | | checkMethod: ({ row }) => row?.userId !== 1, |
| | | }, |
| | | columns, |
| | | height: 'auto', |
| | | keepSource: true, |
| | | pagerConfig: {}, |
| | | proxyConfig: { |
| | | ajax: { |
| | | // è·å页颿°æ®ï¼æç´¢ï¼éç½®ä¹æ¯ |
| | | /* |
| | | @params page:页ç åæ° |
| | | @params formValues:表ååæ° |
| | | */ |
| | | query: async ({ page }, formValues = {}) => { |
| | | console.log("è·å页颿°æ®ï¼") |
| | | const res = { |
| | | rows:[ { |
| | | id:"0", //å·¥ä½idï¼ç¼å· |
| | | workName:"å·¥ä½åç§°1", //å·¥ä½åç§° |
| | | workClass:"å·¥ä½ç±»å«", //å·¥ä½ç±»å« |
| | | workContent:"å·¥ä½å
容", //å·¥ä½å
容 |
| | | projectBudget:"项ç®é¢ç®", //项ç®é¢ç® |
| | | amountProject:"项ç®éé¢", //项ç®éé¢ |
| | | responsibleDepartment:"è´è´£é¨é¨", //è´è´£é¨é¨ |
| | | Head:"è´è´£äºº", //è´è´£äºº |
| | | Annual:"年度", //年度 |
| | | assessmentTime:"èæ ¸æ¶é´",//èæ ¸æ¶é´ |
| | | assessmentIndicators:"èæ ¸ææ ", //èæ ¸ææ |
| | | File:"æä»¶å°å", //æä»¶ä¸ä¼ |
| | | taskStatus:"ä»»å¡ç¶æ", //ä»»å¡ç¶æ |
| | | assignmentStatus:"åé
ç¶æ", //åé
ç¶æ |
| | | workProgress:"å·¥ä½è¿åº¦" //å·¥ä½è¿åº¦ |
| | | } ], |
| | | total:1 |
| | | } |
| | | console.log( "res", res) |
| | | return res |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'userId', |
| | | height: 48, |
| | | }, |
| | | id: 'system-user-index', |
| | | }; |
| | | const [BasicTable, tableApi] = useVbenVxeGrid({ |
| | | formOptions, |
| | | gridOptions, |
| | | }); |
| | | // æ¥ç详æ
|
| | | const [UserDrawer, userDrawerApi] = useVbenDrawer({ |
| | | connectedComponent: userDrawer, |
| | | }); |
| | | // å·¥ä½ä¸å |
| | | const [userAssignWorkDrawer, userAssignWorkApi] = useVbenDrawer({ |
| | | connectedComponent: userAssignWork |
| | | }) |
| | | // 工使±æ¥ |
| | | const [userReportDrawer, userReportApi] = useVbenDrawer({ |
| | | connectedComponent: userReport |
| | | }) |
| | | // æ¤æ¹æ³ææªå¯ç¨ |
| | | function handleAdd() { |
| | | userDrawerApi.setData({}); |
| | | userDrawerApi.open(); |
| | | } |
| | | // æ¥ç详æ
|
| | | function handleLook(row: any) { |
| | | userDrawerApi.setData({ id: row.id, look: true }); |
| | | userDrawerApi.open(); |
| | | } |
| | | // å·¥ä½ä¸å |
| | | function handleAssigningWork(row: any) { |
| | | userAssignWorkApi.setData({id:row.id, look:true}) |
| | | userAssignWorkApi.open() |
| | | } |
| | | // 工使±æ¥ |
| | | function handleReport(row: any) { |
| | | userReportApi.setData({ id: row.id, row: row }); |
| | | userReportApi.open(); |
| | | } |
| | | function handleMultiDelete() { |
| | | const rows = tableApi.grid.getCheckboxRecords(); |
| | | const ids = rows.map((row: User) => row.userId); |
| | | Modal.confirm({ |
| | | title: 'æç¤º', |
| | | okType: 'danger', |
| | | content: `确认å é¤éä¸ç${ids.length}æ¡è®°å½åï¼`, |
| | | onOk: async () => { |
| | | await userRemove(ids); |
| | | await tableApi.query(); |
| | | }, |
| | | }); |
| | | } |
| | | |
| | | function handleDownloadExcel() { |
| | | commonDownloadExcel(userExport, 'ç¨æ·ç®¡ç', tableApi.formApi.form.values, { |
| | | fieldMappingTime: formOptions.fieldMappingTime, |
| | | }); |
| | | } |
| | | |
| | | const [UserInfoModal, userInfoModalApi] = useVbenModal({ |
| | | connectedComponent: userInfoModal, |
| | | }); |
| | | function handleUserInfo(row: User) { |
| | | userInfoModalApi.setData({ userId: row.userId }); |
| | | userInfoModalApi.open(); |
| | | } |
| | | |
| | | const [UserResetPwdModal, userResetPwdModalApi] = useVbenModal({ |
| | | connectedComponent: userResetPwdModal, |
| | | }); |
| | | |
| | | function handleResetPwd(record: User) { |
| | | userResetPwdModalApi.setData({ record }); |
| | | userResetPwdModalApi.open(); |
| | | } |
| | | |
| | | const { hasAccessByCodes } = useAccess(); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
| | | <template> |
| | | <Page :auto-content-height="true"> |
| | | <div class="flex h-full gap-[8px]"> |
| | | <BasicTable class="flex-1 overflow-hidden" table-title="å·¥ä½å表"> |
| | | <template #toolbar-tools> |
| | | <Space> |
| | | <!-- å¯¼åº --> |
| | | <!-- <a-button |
| | | @click="handleDownloadExcel" |
| | | > |
| | | {{ $t('pages.common.export') }} |
| | | </a-button> --> |
| | | <!-- 导å
¥ --> |
| | | <!-- <a-button |
| | | v-access:code="['system:user:import']" |
| | | @click="handleImport" |
| | | > |
| | | {{ $t('pages.common.import') }} |
| | | </a-button> --> |
| | | <!-- å é¤ --> |
| | | <a-button |
| | | :disabled="!vxeCheckboxChecked(tableApi)" |
| | | danger |
| | | type="primary" |
| | | @click="handleMultiDelete" |
| | | > |
| | | {{ $t('pages.common.delete') }} |
| | | </a-button> |
| | | <!-- æ°å¢ --> |
| | | <!-- <a-button |
| | | type="primary" |
| | | @click="handleAdd" |
| | | > |
| | | {{ $t('pages.common.WorkIssued') }} |
| | | </a-button> --> |
| | | </Space> |
| | | </template> |
| | | <template #avatar="{ row }"> |
| | | <!-- å¯è½è¦å¤æç©ºå符串æ
åµ æä»¥æ²¡æä½¿ç¨?? --> |
| | | <Avatar :src="row.avatar || preferences.app.defaultAvatar" /> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <TableSwitch |
| | | v-model="row.status" |
| | | :api="() => userStatusChange(row)" |
| | | :disabled=" |
| | | row.userId === 1 || !hasAccessByCodes(['system:user:edit']) |
| | | " |
| | | :reload="() => tableApi.query()" |
| | | /> |
| | | </template> |
| | | <template #action="{ row }"> |
| | | <Space> |
| | | <!-- æ¥ç --> |
| | | <ghost-button |
| | | @click.stop="handleLook(row)" |
| | | > |
| | | {{ $t('pages.common.look') }} |
| | | </ghost-button> |
| | | <!-- æ±æ¥ --> |
| | | <ghost-button |
| | | @click.stop="handleReport(row)" |
| | | > |
| | | {{ $t('pages.common.report') }} |
| | | </ghost-button> |
| | | <!-- å·¥ä½ä¸å --> |
| | | <ghost-button |
| | | @click.stop="handleAssigningWork(row)" |
| | | > |
| | | {{ $t('pages.common.AssigningWork') }} |
| | | </ghost-button> |
| | | </Space> |
| | | </template> |
| | | </BasicTable> |
| | | </div> |
| | | <UserImpotModal @reload="tableApi.query()" /> |
| | | <UserDrawer @reload="tableApi.query()" /> <!-- å·¥ä½è¯¦æ
--> |
| | | <userAssignWorkDrawer @reload="tableApi.query()" /> <!-- å·¥ä½ä¸å --> |
| | | <userReportDrawer @reload="tableApi.query()" /> <!-- 工使±æ¥ --> |
| | | <UserInfoModal /> |
| | | <UserResetPwdModal /> |
| | | </Page> |
| | | </template> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { DescItem } from '#/components/description'; |
| | | |
| | | import { DictEnum } from '@vben/constants'; |
| | | |
| | | import { Tag } from 'ant-design-vue'; |
| | | import dayjs from 'dayjs'; |
| | | import duration from 'dayjs/plugin/duration'; |
| | | import relativeTime from 'dayjs/plugin/relativeTime'; |
| | | |
| | | import { renderDict } from '#/utils/render'; |
| | | |
| | | dayjs.extend(duration); |
| | | dayjs.extend(relativeTime); |
| | | |
| | | function renderTags(list: string[]) { |
| | | return ( |
| | | <div class="flex flex-row flex-wrap gap-0.5"> |
| | | {list.map((item) => ( |
| | | <Tag key={item}>{item}</Tag> |
| | | ))} |
| | | </div> |
| | | ); |
| | | } |
| | | |
| | | export const descSchema: DescItem[] = [ |
| | | { |
| | | field: 'userId', |
| | | label: 'ç¨æ·ID', |
| | | }, |
| | | { |
| | | field: 'status', |
| | | label: 'ç¨æ·ç¶æ', |
| | | render(value) { |
| | | return renderDict(value, DictEnum.SYS_NORMAL_DISABLE); |
| | | }, |
| | | }, |
| | | { |
| | | field: 'nickName', |
| | | label: 'ç¨æ·ä¿¡æ¯', |
| | | render(_, data) { |
| | | const { deptName = 'ææ é¨é¨ä¿¡æ¯', nickName, userName } = data; |
| | | // 为äºå
¼å®¹æ°çæ¬åæ§çæ¬ |
| | | let currentDept = deptName; |
| | | if (data.dept && data.dept.deptName) { |
| | | currentDept = data.dept.deptName; |
| | | } |
| | | return `${userName} / ${nickName} / ${currentDept}`; |
| | | }, |
| | | }, |
| | | { |
| | | field: 'phonenumber', |
| | | label: 'ææºå·', |
| | | render(value) { |
| | | return value || 'æªè®¾ç½®ææºå·ç '; |
| | | }, |
| | | }, |
| | | { |
| | | field: 'email', |
| | | label: 'é®ç®±', |
| | | render(value) { |
| | | return value || 'æªè®¾ç½®é®ç®±å°å'; |
| | | }, |
| | | }, |
| | | { |
| | | field: 'postNames', |
| | | label: 'å²ä½', |
| | | render(value) { |
| | | if (Array.isArray(value) && value.length === 0) { |
| | | return 'ææ ä¿¡æ¯'; |
| | | } |
| | | return renderTags(value); |
| | | }, |
| | | }, |
| | | { |
| | | field: 'roleNames', |
| | | label: 'æé', |
| | | render(value) { |
| | | if (Array.isArray(value) && value.length === 0) { |
| | | return 'ææ ä¿¡æ¯'; |
| | | } |
| | | return renderTags(value); |
| | | }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | label: 'å建æ¶é´', |
| | | }, |
| | | { |
| | | field: 'loginIp', |
| | | label: '䏿¬¡ç»å½IP', |
| | | render(value) { |
| | | return value || <span class="text-orange-500">仿ªç»å½è¿</span>; |
| | | }, |
| | | }, |
| | | { |
| | | field: 'loginDate', |
| | | label: '䏿¬¡ç»å½æ¶é´', |
| | | render(value) { |
| | | if (!value) { |
| | | return <span class="text-orange-500">仿ªç»å½è¿</span>; |
| | | } |
| | | // é»è®¤enæ¾ç¤º |
| | | dayjs.locale('zh-cn'); |
| | | // 计ç®ç¸å·®ç§æ° |
| | | const diffSeconds = dayjs().diff(dayjs(value), 'second'); |
| | | /** |
| | | * 转为æ¶é´æ¾ç¤º(xæ x天) |
| | | * https://dayjs.fenxianglu.cn/category/duration.html#%E4%BA%BA%E6%80%A7%E5%8C%96 |
| | | * |
| | | */ |
| | | const diffText = dayjs.duration(diffSeconds, 'seconds').humanize(); |
| | | return ( |
| | | <div class="flex gap-2"> |
| | | {value} |
| | | <Tag bordered={false} color="cyan"> |
| | | {diffText}å |
| | | </Tag> |
| | | </div> |
| | | ); |
| | | }, |
| | | }, |
| | | { |
| | | field: 'remark', |
| | | label: '夿³¨', |
| | | render(value) { |
| | | return value || 'æ '; |
| | | }, |
| | | }, |
| | | ]; |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup lang="ts"> |
| | | import type { Role } from '#/api/system/user/model'; |
| | | |
| | | import { computed, h, ref } from 'vue'; |
| | | |
| | | import { useVbenDrawer } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | import { cloneDeep } from '@vben/utils'; |
| | | |
| | | import { Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { findUserInfo, userAdd, userUpdate } from '#/api/system/user'; |
| | | import { authScopeOptions } from '#/views/system/role/data'; |
| | | |
| | | import { drawerSchema, AssigningWorkSchema } from './data'; |
| | | import { deptList } from '#/api/system/dept/index'; |
| | | |
| | | const emit = defineEmits<{ reload: [] }>(); |
| | | |
| | | const isUpdate = ref(false); //å½åæ¯å¦ä¸ºç¼è¾ |
| | | const isLook = ref(false); //å½åæ¯å¦ä¸ºæ¥ç |
| | | const title = computed(() => { |
| | | let text = ''; |
| | | text = "å·¥ä½ä¸å" |
| | | return text; |
| | | }); |
| | | |
| | | const [BasicForm, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 80, |
| | | }, |
| | | schema: AssigningWorkSchema(), |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-2', |
| | | }); |
| | | |
| | | /** |
| | | * çæè§è²çèªå®ä¹label |
| | | * ä¹å¯ä»¥ç¨optionææ§½æ¥å |
| | | * renderComponentContent: () => ({ |
| | | option: ({value, label, [disabled, key, title]}) => '', |
| | | }), |
| | | */ |
| | | function genRoleOptionlabel(role: Role) { |
| | | const found = authScopeOptions.find((item) => item.value === role.dataScope); |
| | | if (!found) { |
| | | return role.roleName; |
| | | } |
| | | return h('div', { class: 'flex items-center gap-[6px]' }, [ |
| | | h('span', null, role.roleName), |
| | | h(Tag, { color: found.color }, () => found.label), |
| | | ]); |
| | | } |
| | | // æå¼ä»¥åç¼è¾é½ä¼è°ç¨è¯¥äºä»¶ |
| | | const [BasicDrawer, drawerApi] = useVbenDrawer({ |
| | | onCancel: handleCancel, |
| | | onConfirm: handleConfirm, |
| | | async onOpenChange(isOpen) { |
| | | drawerApi.drawerLoading(true); |
| | | if (isOpen) { |
| | | // åå§åä¸ä¸è¡¨åé项 |
| | | const deptlist = await ( |
| | | await deptList() |
| | | ).map((item) => { |
| | | return { |
| | | ...item, |
| | | value: item.deptId, |
| | | label: item.deptName, |
| | | }; |
| | | }); |
| | | formApi.updateSchema([ |
| | | { |
| | | componentProps: { options: deptlist }, |
| | | fieldName: 'responsibleDepartment', |
| | | }, |
| | | ]); |
| | | } |
| | | |
| | | if (!isOpen) { |
| | | // éè¦éç½®å²ä½éæ© |
| | | formApi.updateSchema([ |
| | | { |
| | | componentProps: { options: [], placeholder: '请å
éæ©é¨é¨' }, |
| | | fieldName: 'postIds', |
| | | }, |
| | | ]); |
| | | return null; |
| | | } |
| | | const { id } = drawerApi.getData() as { id?: number | string }; |
| | | isUpdate.value = !!id; |
| | | |
| | | // æ´æ° && èµå¼ |
| | | // const { postIds, posts, roleIds, roles, user } = await findUserInfo(id); //è°ç¨æ¥å£è·å详ç»ä¿¡æ¯ |
| | | |
| | | const data = { |
| | | id: '0', //å·¥ä½idï¼ç¼å· |
| | | workName: 'å·¥ä½åç§°1', //å·¥ä½åç§° |
| | | workClass: 'å·¥ä½ç±»å«', //å·¥ä½ç±»å« |
| | | workContent: 'å·¥ä½å
容', //å·¥ä½å
容 |
| | | projectBudget: '项ç®é¢ç®', //项ç®é¢ç® |
| | | amountProject: '项ç®éé¢', //项ç®éé¢ |
| | | responsibleDepartment: 'è´è´£é¨é¨', //è´è´£é¨é¨ |
| | | Head: 'è´è´£äºº', //è´è´£äºº |
| | | Annual: '2025', //年度 |
| | | assessmentTime: '2025-06-25', //èæ ¸æ¶é´ |
| | | assessmentIndicators: 'èæ ¸ææ ', //èæ ¸ææ |
| | | File: 'æä»¶å°å', //æä»¶ä¸ä¼ |
| | | taskStatus: 'ä»»å¡ç¶æ', //ä»»å¡ç¶æ |
| | | assignmentStatus: 'åé
ç¶æ', //åé
ç¶æ |
| | | workProgress: 'å·¥ä½è¿åº¦', //å·¥ä½è¿åº¦ |
| | | }; |
| | | |
| | | console.log(data); |
| | | console.log(id); |
| | | |
| | | if (data && id) { |
| | | await Promise.all([ |
| | | // æ·»å åºç¡ä¿¡æ¯ |
| | | formApi.setValues(data), |
| | | ]); |
| | | } |
| | | drawerApi.drawerLoading(false); |
| | | console.log(isLook.value); |
| | | }, |
| | | }); |
| | | // 表åæäº¤ï¼ç¼è¾ä¸æ°å¢é½ä¼è°ç¨è¿ä¸ªæ¹æ³ä½ |
| | | async function handleConfirm() { |
| | | try { |
| | | drawerApi.drawerLoading(true); |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | const data = cloneDeep(await formApi.getValues()); //表åå
çæ°æ® |
| | | console.log(data) |
| | | // await (isUpdate.value ? userUpdate(data) : userAdd(data)); |
| | | emit('reload'); |
| | | await handleCancel(); |
| | | } catch (error) { |
| | | console.error(error); |
| | | } finally { |
| | | drawerApi.drawerLoading(false); |
| | | } |
| | | } |
| | | |
| | | async function handleCancel() { |
| | | drawerApi.close(); |
| | | await formApi.resetForm(); |
| | | } |
| | | |
| | | function IsShowConfirmButton(params: type) { |
| | | if (isLook.value) { |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | // å¨è¿å è½½äºæ°æ® |
| | | </script> |
| | | |
| | | <template> |
| | | <BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]"> |
| | | <BasicForm /> |
| | | </BasicDrawer> |
| | | </template> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup lang="ts"> |
| | | import type { Role } from '#/api/system/user/model'; |
| | | |
| | | import { computed, h, ref } from 'vue'; |
| | | |
| | | import { useVbenDrawer } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | import { cloneDeep } from '@vben/utils'; |
| | | |
| | | import { Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { findUserInfo, userAdd, userUpdate } from '#/api/system/user'; |
| | | import { authScopeOptions } from '#/views/system/role/data'; |
| | | |
| | | import { drawerSchema } from './data'; |
| | | import { deptList } from '#/api/system/dept/index'; |
| | | import { message, Upload } from 'ant-design-vue'; |
| | | |
| | | const emit = defineEmits<{ reload: [] }>(); |
| | | |
| | | const isUpdate = ref(false); //å½åæ¯å¦ä¸ºç¼è¾ |
| | | const isLook = ref(false); //å½åæ¯å¦ä¸ºæ¥ç |
| | | const title = computed(() => { |
| | | let text = ''; |
| | | if (isLook) { |
| | | text = $t('pages.common.look'); |
| | | } else if (isUpdate.value) { |
| | | text = $t('pages.common.edit'); |
| | | } else if (!isUpdate.value) { |
| | | text = $t('pages.common.add'); |
| | | } |
| | | return text; |
| | | }); |
| | | |
| | | const [BasicForm, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 80, |
| | | }, |
| | | schema: drawerSchema(), |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-2', |
| | | }); |
| | | |
| | | /** |
| | | * çæè§è²çèªå®ä¹label |
| | | * ä¹å¯ä»¥ç¨optionææ§½æ¥å |
| | | * renderComponentContent: () => ({ |
| | | option: ({value, label, [disabled, key, title]}) => '', |
| | | }), |
| | | */ |
| | | function genRoleOptionlabel(role: Role) { |
| | | const found = authScopeOptions.find((item) => item.value === role.dataScope); |
| | | if (!found) { |
| | | return role.roleName; |
| | | } |
| | | return h('div', { class: 'flex items-center gap-[6px]' }, [ |
| | | h('span', null, role.roleName), |
| | | h(Tag, { color: found.color }, () => found.label), |
| | | ]); |
| | | } |
| | | // æå¼ä»¥åç¼è¾é½ä¼è°ç¨è¯¥äºä»¶ |
| | | const [BasicDrawer, drawerApi] = useVbenDrawer({ |
| | | onCancel: handleCancel, |
| | | onConfirm: handleConfirm, |
| | | async onOpenChange(isOpen) { |
| | | drawerApi.drawerLoading(true); |
| | | if (isOpen) { |
| | | let deptlist; |
| | | try { |
| | | // åå§åä¸ä¸è¡¨åé项 |
| | | deptlist = await ( |
| | | await deptList() |
| | | ).map((item) => { |
| | | return { |
| | | ...item, |
| | | value: item.deptId, |
| | | label: item.deptName, |
| | | }; |
| | | }); |
| | | } catch (error) { |
| | | console.log( "Api-deptList", error ) |
| | | } |
| | | formApi.updateSchema([ |
| | | { |
| | | componentProps: { options: deptlist }, |
| | | fieldName: 'responsibleDepartment', |
| | | }, |
| | | ]); |
| | | } |
| | | if (!isOpen) { |
| | | // éè¦éç½®å²ä½éæ© |
| | | formApi.updateSchema([ |
| | | { |
| | | componentProps: { options: [], placeholder: '请å
éæ©é¨é¨' }, |
| | | fieldName: 'postIds', |
| | | }, |
| | | ]); |
| | | return null; |
| | | } |
| | | const { id } = drawerApi.getData() as { id?: number | string }; |
| | | const { look } = drawerApi.getData() as { look?: boolean }; |
| | | isUpdate.value = !!id; |
| | | isLook.value = !!look; |
| | | console.log('isLook.value', isLook.value); |
| | | /** updateæ¶ ç¦ç¨ç¨æ·åä¿®æ¹ ä¸æ¾ç¤ºå¯ç æ¡ */ |
| | | // å¦æä¸ºæ¥ç忿项é½ä¸å¯ç¼è¾ |
| | | formApi.updateSchema([ |
| | | { componentProps: { disabled: isLook.value }, fieldName: 'workName' }, |
| | | { componentProps: { disabled: isLook.value }, fieldName: 'workClass' }, |
| | | { componentProps: { disabled: isLook.value }, fieldName: 'workContent' }, |
| | | { |
| | | componentProps: { disabled: isLook.value }, |
| | | fieldName: 'projectBudget', |
| | | }, |
| | | { |
| | | componentProps: { disabled: isLook.value }, |
| | | fieldName: 'amountProject', |
| | | }, |
| | | { |
| | | componentProps: { disabled: isLook.value }, |
| | | fieldName: 'responsibleDepartment', |
| | | }, |
| | | { componentProps: { disabled: isLook.value }, fieldName: 'Head' }, |
| | | { componentProps: { disabled: isLook.value }, fieldName: 'Annual' }, |
| | | { |
| | | componentProps: { disabled: isLook.value }, |
| | | fieldName: 'assessmentTime', |
| | | }, |
| | | { |
| | | componentProps: { disabled: isLook.value }, |
| | | fieldName: 'assessmentIndicators', |
| | | }, |
| | | { componentProps: { disabled: isLook.value }, fieldName: 'File' }, |
| | | ]); |
| | | |
| | | drawerApi.setState({ |
| | | showConfirmButton: !isLook.value, |
| | | }); |
| | | |
| | | // æ´æ° && èµå¼ |
| | | // const { postIds, posts, roleIds, roles, user } = await findUserInfo(id); //è°ç¨æ¥å£è·å详ç»ä¿¡æ¯ |
| | | |
| | | const data = { |
| | | id: '0', //å·¥ä½idï¼ç¼å· |
| | | workName: 'å·¥ä½åç§°1', //å·¥ä½åç§° |
| | | workClass: 'å·¥ä½ç±»å«', //å·¥ä½ç±»å« |
| | | workContent: 'å·¥ä½å
容', //å·¥ä½å
容 |
| | | projectBudget: '项ç®é¢ç®', //项ç®é¢ç® |
| | | amountProject: '项ç®éé¢', //项ç®éé¢ |
| | | responsibleDepartment: 'è´è´£é¨é¨', //è´è´£é¨é¨ |
| | | Head: 'è´è´£äºº', //è´è´£äºº |
| | | Annual: '2025', //年度 |
| | | assessmentTime: '2025-06-25', //èæ ¸æ¶é´ |
| | | assessmentIndicators: 'èæ ¸ææ ', //èæ ¸ææ |
| | | File: 'æä»¶å°å', //æä»¶ä¸ä¼ |
| | | taskStatus: 'ä»»å¡ç¶æ', //ä»»å¡ç¶æ |
| | | assignmentStatus: 'åé
ç¶æ', //åé
ç¶æ |
| | | workProgress: 'å·¥ä½è¿åº¦', //å·¥ä½è¿åº¦ |
| | | }; |
| | | |
| | | console.log(data); |
| | | console.log(id); |
| | | |
| | | if (data && id) { |
| | | await Promise.all([ |
| | | // æ·»å åºç¡ä¿¡æ¯ |
| | | formApi.setValues(data), |
| | | ]); |
| | | } |
| | | drawerApi.drawerLoading(false); |
| | | console.log(isLook.value); |
| | | }, |
| | | }); |
| | | // 表åæäº¤ï¼ç¼è¾ä¸æ°å¢é½ä¼è°ç¨è¿ä¸ªæ¹æ³ä½ |
| | | async function handleConfirm() { |
| | | try { |
| | | drawerApi.drawerLoading(true); |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | const data = cloneDeep(await formApi.getValues()); //表åå
çæ°æ® |
| | | console.log(data); |
| | | // await (isUpdate.value ? userUpdate(data) : userAdd(data)); |
| | | emit('reload'); |
| | | await handleCancel(); |
| | | } catch (error) { |
| | | console.error(error); |
| | | } finally { |
| | | drawerApi.drawerLoading(false); |
| | | } |
| | | } |
| | | |
| | | async function handleCancel() { |
| | | drawerApi.close(); |
| | | await formApi.resetForm(); |
| | | } |
| | | |
| | | function IsShowConfirmButton(params: type) { |
| | | if (isLook.value) { |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | // å¨è¿å è½½äºæ°æ® |
| | | </script> |
| | | |
| | | <template> |
| | | <BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]"> |
| | | <BasicForm /> |
| | | </BasicDrawer> |
| | | </template> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup lang="ts"> |
| | | import type { UploadFile } from 'ant-design-vue/es/upload/interface'; |
| | | |
| | | import { h, ref, unref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { ExcelIcon, InBoxIcon } from '@vben/icons'; |
| | | |
| | | import { Modal, Switch, Upload } from 'ant-design-vue'; |
| | | |
| | | import { downloadImportTemplate, userImportData } from '#/api/system/user'; |
| | | import { commonDownloadExcel } from '#/utils/file/download'; |
| | | |
| | | const emit = defineEmits<{ reload: [] }>(); |
| | | |
| | | const UploadDragger = Upload.Dragger; |
| | | |
| | | const [BasicModal, modalApi] = useVbenModal({ |
| | | onCancel: handleCancel, |
| | | onConfirm: handleSubmit, |
| | | }); |
| | | |
| | | const fileList = ref<UploadFile[]>([]); |
| | | const checked = ref(false); |
| | | |
| | | async function handleSubmit() { |
| | | try { |
| | | modalApi.modalLoading(true); |
| | | if (fileList.value.length !== 1) { |
| | | handleCancel(); |
| | | return; |
| | | } |
| | | const data = { |
| | | file: fileList.value[0]!.originFileObj as Blob, |
| | | updateSupport: unref(checked), |
| | | }; |
| | | const { code, msg } = await userImportData(data); |
| | | let modal = Modal.success; |
| | | if (code === 200) { |
| | | emit('reload'); |
| | | } else { |
| | | modal = Modal.error; |
| | | } |
| | | handleCancel(); |
| | | modal({ |
| | | content: h('div', { |
| | | class: 'max-h-[260px] overflow-y-auto', |
| | | innerHTML: msg, // åå°å·²ç»å¤çxssé®é¢ |
| | | }), |
| | | title: 'æç¤º', |
| | | }); |
| | | } catch (error) { |
| | | console.warn(error); |
| | | modalApi.close(); |
| | | } finally { |
| | | modalApi.modalLoading(false); |
| | | } |
| | | } |
| | | |
| | | function handleCancel() { |
| | | modalApi.close(); |
| | | fileList.value = []; |
| | | checked.value = false; |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <BasicModal |
| | | :close-on-click-modal="false" |
| | | :fullscreen-button="false" |
| | | title="ç¨æ·å¯¼å
¥" |
| | | > |
| | | <!-- z-indexä¸è®¾ç½®ä¼é®æ¡æ¨¡æ¿ä¸è½½loading --> |
| | | <!-- æå¨å¤ç è䏿¯æ¾å
¥æä»¶å°±ä¸ä¼ --> |
| | | <UploadDragger |
| | | v-model:file-list="fileList" |
| | | :before-upload="() => false" |
| | | :max-count="1" |
| | | :show-upload-list="true" |
| | | accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" |
| | | > |
| | | <p class="ant-upload-drag-icon flex items-center justify-center"> |
| | | <InBoxIcon class="text-primary size-[48px]" /> |
| | | </p> |
| | | <p class="ant-upload-text">ç¹å»æè
ææ½å°æ¤å¤ä¸ä¼ æä»¶</p> |
| | | </UploadDragger> |
| | | <div class="mt-2 flex flex-col gap-2"> |
| | | <div class="flex items-center gap-2"> |
| | | <span>å
许导å
¥xlsx, xlsæä»¶</span> |
| | | <a-button |
| | | type="link" |
| | | @click="commonDownloadExcel(downloadImportTemplate, 'ç¨æ·å¯¼å
¥æ¨¡æ¿')" |
| | | > |
| | | <div class="flex items-center gap-[4px]"> |
| | | <ExcelIcon /> |
| | | <span>ä¸è½½æ¨¡æ¿</span> |
| | | </div> |
| | | </a-button> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span :class="{ 'text-red-500': checked }"> |
| | | æ¯å¦æ´æ°/è¦çå·²åå¨çç¨æ·æ°æ® |
| | | </span> |
| | | <Switch v-model:checked="checked" /> |
| | | </div> |
| | | </div> |
| | | </BasicModal> |
| | | </template> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup lang="ts"> |
| | | import type { User } from '#/api/system/user/model'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { findUserInfo } from '#/api/system/user'; |
| | | import { Description, useDescription } from '#/components/description'; |
| | | |
| | | import { descSchema } from './info'; |
| | | |
| | | const [BasicModal, modalApi] = useVbenModal({ |
| | | onOpenChange: handleOpenChange, |
| | | }); |
| | | |
| | | const [registerDescription, { setDescProps }] = useDescription({ |
| | | column: 1, |
| | | labelStyle: { |
| | | minWidth: '150px', |
| | | width: '150px', |
| | | }, |
| | | schema: descSchema, |
| | | }); |
| | | |
| | | async function handleOpenChange(open: boolean) { |
| | | if (!open) { |
| | | return null; |
| | | } |
| | | modalApi.modalLoading(true); |
| | | |
| | | const { userId } = modalApi.getData() as { userId: number | string }; |
| | | const response = await findUserInfo(userId); |
| | | // å¤é¨çroleIds postIdsææ¯çæ£å¯¹åºç æ°å¢æ¶ä¸ºç©º |
| | | // postsæä¸ºNullçæ
åµ éè¦ç»é»è®¤å¼ |
| | | const { postIds = [], posts = [], roleIds = [], roles = [], user } = response; |
| | | |
| | | const postNames = posts |
| | | .filter((item) => postIds.includes(item.postId)) |
| | | .map((item) => item.postName); |
| | | |
| | | const roleNames = roles |
| | | .filter((item) => roleIds.includes(item.roleId)) |
| | | .map((item) => item.roleName); |
| | | |
| | | interface UserWithNames extends User { |
| | | postNames: string[]; |
| | | roleNames: string[]; |
| | | } |
| | | (user as UserWithNames).postNames = postNames; |
| | | (user as UserWithNames).roleNames = roleNames; |
| | | |
| | | // èµå¼ |
| | | setDescProps({ data: user }); |
| | | |
| | | modalApi.modalLoading(false); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <BasicModal :footer="false" :fullscreen-button="false" title="ç¨æ·ä¿¡æ¯"> |
| | | <Description @register="registerDescription" /> |
| | | </BasicModal> |
| | | </template> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup lang="ts"> |
| | | import type { Role } from '#/api/system/user/model'; |
| | | |
| | | import { computed, h, ref } from 'vue'; |
| | | |
| | | import { useVbenDrawer } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | import { cloneDeep } from '@vben/utils'; |
| | | |
| | | import { Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { findUserInfo, userAdd, userUpdate } from '#/api/system/user'; |
| | | import { authScopeOptions } from '#/views/system/role/data'; |
| | | |
| | | import { drawerSchema, ReportScheme } from './data'; |
| | | import { deptList } from '#/api/system/dept/index'; |
| | | |
| | | const emit = defineEmits<{ reload: [] }>(); |
| | | |
| | | const isUpdate = ref(false); //å½åæ¯å¦ä¸ºç¼è¾ |
| | | const isLook = ref(false); //å½åæ¯å¦ä¸ºæ¥ç |
| | | const title = computed(() => { |
| | | let text = ''; |
| | | text="æ±æ¥" |
| | | return text; |
| | | }); |
| | | |
| | | const [BasicForm, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 80, |
| | | }, |
| | | schema: ReportScheme(), |
| | | showDefaultActions: false, |
| | | wrapperClass: 'grid-cols-2', |
| | | }); |
| | | |
| | | /** |
| | | * çæè§è²çèªå®ä¹label |
| | | * ä¹å¯ä»¥ç¨optionææ§½æ¥å |
| | | * renderComponentContent: () => ({ |
| | | option: ({value, label, [disabled, key, title]}) => '', |
| | | }), |
| | | */ |
| | | function genRoleOptionlabel(role: Role) { |
| | | const found = authScopeOptions.find((item) => item.value === role.dataScope); |
| | | if (!found) { |
| | | return role.roleName; |
| | | } |
| | | return h('div', { class: 'flex items-center gap-[6px]' }, [ |
| | | h('span', null, role.roleName), |
| | | h(Tag, { color: found.color }, () => found.label), |
| | | ]); |
| | | } |
| | | // æå¼ä»¥åç¼è¾é½ä¼è°ç¨è¯¥äºä»¶ |
| | | const [BasicDrawer, drawerApi] = useVbenDrawer({ |
| | | onCancel: handleCancel, |
| | | onConfirm: handleConfirm, |
| | | async onOpenChange(isOpen) { |
| | | drawerApi.drawerLoading(true); |
| | | if (isOpen) { |
| | | let deptlist; |
| | | try { |
| | | // åå§åä¸ä¸è¡¨åé项 |
| | | deptlist = await ( |
| | | await deptList() |
| | | ).map((item) => { |
| | | return { |
| | | ...item, |
| | | value: item.deptId, |
| | | label: item.deptName, |
| | | }; |
| | | }); |
| | | } catch (error) { |
| | | console.error('uReport-Api-deptlist', error); |
| | | } |
| | | formApi.updateSchema([ |
| | | { |
| | | componentProps: { options: deptlist }, |
| | | fieldName: 'responsibleDepartment', |
| | | }, |
| | | ]); |
| | | } |
| | | |
| | | if (!isOpen) { |
| | | // éè¦éç½®å²ä½éæ© |
| | | formApi.updateSchema([ |
| | | { |
| | | componentProps: { options: [], placeholder: '请å
éæ©é¨é¨' }, |
| | | fieldName: 'postIds', |
| | | }, |
| | | ]); |
| | | return null; |
| | | } |
| | | const { id } = drawerApi.getData() as { id?: number | string }; |
| | | isUpdate.value = !!id; |
| | | /** updateæ¶ ç¦ç¨ç¨æ·åä¿®æ¹ ä¸æ¾ç¤ºå¯ç æ¡ */ |
| | | |
| | | // æ´æ° && èµå¼ |
| | | // const { postIds, posts, roleIds, roles, user } = await findUserInfo(id); //è°ç¨æ¥å£è·å详ç»ä¿¡æ¯ |
| | | |
| | | const data = { |
| | | id: '0', //å·¥ä½idï¼ç¼å· |
| | | workName: 'å·¥ä½åç§°1', //å·¥ä½åç§° |
| | | workClass: 'å·¥ä½ç±»å«', //å·¥ä½ç±»å« |
| | | workContent: '', //å·¥ä½å
容 |
| | | projectBudget: '项ç®é¢ç®', //项ç®é¢ç® |
| | | amountProject: '项ç®éé¢', //项ç®éé¢ |
| | | responsibleDepartment: 'è´è´£é¨é¨', //è´è´£é¨é¨ |
| | | Head: 'è´è´£äºº', //è´è´£äºº |
| | | Annual: '2025', //年度 |
| | | assessmentTime: '2025-06-25', //èæ ¸æ¶é´ |
| | | assessmentIndicators: 'èæ ¸ææ ', //èæ ¸ææ |
| | | File: 'æä»¶å°å', //æä»¶ä¸ä¼ |
| | | taskStatus: 'ä»»å¡ç¶æ', //ä»»å¡ç¶æ |
| | | assignmentStatus: 'åé
ç¶æ', //åé
ç¶æ |
| | | workProgress: 'å·¥ä½è¿åº¦', //å·¥ä½è¿åº¦ |
| | | }; |
| | | |
| | | console.log(data); |
| | | console.log(id); |
| | | |
| | | if (data && id) { |
| | | await Promise.all([ |
| | | // æ·»å åºç¡ä¿¡æ¯ |
| | | formApi.setValues(data), |
| | | ]); |
| | | } |
| | | drawerApi.drawerLoading(false); |
| | | console.log(isLook.value); |
| | | }, |
| | | }); |
| | | // 表åæäº¤ï¼ç¼è¾ä¸æ°å¢é½ä¼è°ç¨è¿ä¸ªæ¹æ³ä½ |
| | | async function handleConfirm() { |
| | | try { |
| | | drawerApi.drawerLoading(true); |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | const data = cloneDeep(await formApi.getValues()); //表åå
çæ°æ® |
| | | console.log(data); |
| | | // await (isUpdate.value ? userUpdate(data) : userAdd(data)); |
| | | emit('reload'); |
| | | await handleCancel(); |
| | | } catch (error) { |
| | | console.error(error); |
| | | } finally { |
| | | drawerApi.drawerLoading(false); |
| | | } |
| | | } |
| | | |
| | | async function handleCancel() { |
| | | drawerApi.close(); |
| | | await formApi.resetForm(); |
| | | } |
| | | |
| | | function IsShowConfirmButton(params: type) { |
| | | if (isLook.value) { |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | // å¨è¿å è½½äºæ°æ® |
| | | </script> |
| | | |
| | | <template> |
| | | <BasicDrawer :close-on-click-modal="false" :title="title" class="w-[600px]"> |
| | | <BasicForm /> |
| | | </BasicDrawer> |
| | | </template> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <script setup lang="ts"> |
| | | import type { ResetPwdParam, User } from '#/api/system/user/model'; |
| | | |
| | | import { useVbenModal, z } from '@vben/common-ui'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { userResetPassword } from '#/api/system/user'; |
| | | import { Description, useDescription } from '#/components/description'; |
| | | |
| | | const emit = defineEmits<{ reload: [] }>(); |
| | | |
| | | const [BasicModal, modalApi] = useVbenModal({ |
| | | onCancel: handleCancel, |
| | | onConfirm: handleSubmit, |
| | | onOpenChange: handleOpenChange, |
| | | }); |
| | | |
| | | const [registerDescription, { setDescProps }] = useDescription({ |
| | | column: 1, |
| | | schema: [ |
| | | { |
| | | field: 'userId', |
| | | label: 'ç¨æ·ID', |
| | | }, |
| | | { |
| | | field: 'userName', |
| | | label: 'ç¨æ·å', |
| | | }, |
| | | { |
| | | field: 'nickName', |
| | | label: 'æµç§°', |
| | | }, |
| | | ], |
| | | }); |
| | | |
| | | const [BasicForm, formApi] = useVbenForm({ |
| | | schema: [ |
| | | { |
| | | component: 'Input', |
| | | dependencies: { |
| | | show: () => false, |
| | | triggerFields: [''], |
| | | }, |
| | | fieldName: 'userId', |
| | | label: 'ç¨æ·ID', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | component: 'InputPassword', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ°çå¯ç , å¯ç é¿åº¦ä¸º5 - 20', |
| | | }, |
| | | fieldName: 'password', |
| | | label: 'æ°çå¯ç ', |
| | | rules: z |
| | | .string() |
| | | .min(5, { message: 'å¯ç é¿åº¦ä¸º5 - 20' }) |
| | | .max(20, { message: 'å¯ç é¿åº¦ä¸º5 - 20' }), |
| | | }, |
| | | ], |
| | | showDefaultActions: false, |
| | | commonConfig: { |
| | | labelWidth: 80, |
| | | }, |
| | | }); |
| | | |
| | | async function handleOpenChange(open: boolean) { |
| | | if (!open) { |
| | | return null; |
| | | } |
| | | const { record } = modalApi.getData() as { record: User }; |
| | | setDescProps({ data: record }, true); |
| | | await formApi.setValues({ userId: record.userId }); |
| | | } |
| | | |
| | | async function handleSubmit() { |
| | | try { |
| | | modalApi.modalLoading(true); |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | const data = await formApi.getValues(); |
| | | await userResetPassword(data as ResetPwdParam); |
| | | emit('reload'); |
| | | handleCancel(); |
| | | } catch (error) { |
| | | console.error(error); |
| | | } finally { |
| | | modalApi.modalLoading(false); |
| | | } |
| | | } |
| | | |
| | | async function handleCancel() { |
| | | modalApi.close(); |
| | | await formApi.resetForm(); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <BasicModal |
| | | :close-on-click-modal="false" |
| | | :fullscreen-button="false" |
| | | title="éç½®å¯ç " |
| | | > |
| | | <div class="flex flex-col gap-[12px]"> |
| | | <Description @register="registerDescription" /> |
| | | <BasicForm /> |
| | | </div> |
| | | </BasicModal> |
| | | </template> |
| | |
| | | WF_BUSINESS_STATUS = 'wf_business_status', // ä¸å¡ç¶æ |
| | | WF_FORM_TYPE = 'wf_form_type', // 表åç±»å |
| | | WF_TASK_STATUS = 'wf_task_status', // ä»»å¡ç¶æ |
| | | TASK_STATUS = 'task_status', // ä»»å¡ç¶æ |
| | | ASSIGNMENT_STATUS = 'assignment_status', // åé
ç¶æ |
| | | ANNUAL = 'annual' //年度 |
| | | } |
| | |
| | | </div> |
| | | |
| | | <Transition name="slide-up"> |
| | | <FormMessage class="absolute bottom-1" /> |
| | | <FormMessage class="absolute" /> |
| | | </Transition> |
| | | </div> |
| | | </FormItem> |
| | |
| | | "enabled": "Enabled", |
| | | "disabled": "Disabled", |
| | | "edit": "Edit", |
| | | "look": "Look", |
| | | "delete": "Delete", |
| | | "create": "Create", |
| | | "yes": "Yes", |
| | |
| | | "enabled": "å·²å¯ç¨", |
| | | "disabled": "å·²ç¦ç¨", |
| | | "edit": "ä¿®æ¹", |
| | | "look": "æ¥ç", |
| | | "delete": "å é¤", |
| | | "create": "æ°å¢", |
| | | "yes": "æ¯", |