From 48954e86178c5c3d95f64b59d9a88f22a51ff1ec Mon Sep 17 00:00:00 2001
From: Flex <q1406482700@163.com>
Date: 星期四, 26 六月 2025 16:08:28 +0800
Subject: [PATCH] 修改校验文本错误以及功能补充

---
 ruoyi-ui/apps/web-antd/src/views/work/Issued/index.vue                |  300 +++++
 ruoyi-ui/apps/web-antd/src/views/work/myWork/user-assign-work.vue     |  170 +++
 ruoyi-ui/apps/web-antd/src/adapter/component/index.ts                 |    4 
 ruoyi-ui/apps/web-antd/src/views/work/Issued/user-import-modal.vue    |  108 ++
 ruoyi-ui/apps/web-antd/src/views/work/myWork/index.vue                |  303 +++++
 ruoyi-ui/apps/web-antd/src/components/upload/src/new-file-upload.vue  |  247 ++++
 ruoyi-ui/packages/locales/src/langs/zh-CN/common.json                 |    1 
 ruoyi-ui/apps/web-antd/src/views/work/myWork/dept-tree.vue            |  128 ++
 ruoyi-ui/apps/web-antd/src/views/work/myWork/user-reset-pwd-modal.vue |  111 ++
 ruoyi-ui/apps/web-antd/src/views/work/Issued/user-info-modal.vue      |   62 +
 ruoyi-ui/apps/web-antd/src/views/work/Issued/user-drawer.vue          |  213 +++
 ruoyi-ui/apps/web-antd/src/views/work/Issued/dept-tree.vue            |  128 ++
 ruoyi-ui/apps/web-antd/src/views/work/myWork/data.tsx                 |  262 ++++
 ruoyi-ui/apps/web-antd/src/views/work/myWork/user-drawer.vue          |  218 ++++
 ruoyi-ui/packages/@core/base/shared/src/constants/dict-enum.ts        |    3 
 ruoyi-ui/apps/web-antd/src/views/work/myWork/info.tsx                 |  129 ++
 ruoyi-ui/apps/web-antd/src/locales/langs/en-US/pages.json             |    4 
 ruoyi-ui/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue |    2 
 ruoyi-ui/packages/locales/src/langs/en-US/common.json                 |    1 
 ruoyi-ui/apps/web-antd/src/components/upload/index.ts                 |    1 
 ruoyi-ui/apps/web-antd/src/views/work/myWork/user-report.vue          |  176 +++
 ruoyi-ui/apps/web-antd/src/locales/langs/zh-CN/pages.json             |    4 
 ruoyi-ui/apps/web-antd/src/views/work/Issued/user-reset-pwd-modal.vue |  111 ++
 ruoyi-ui/apps/web-antd/src/views/work/myWork/user-import-modal.vue    |  108 ++
 ruoyi-ui/apps/web-antd/src/views/work/Issued/data.tsx                 |  233 ++++
 ruoyi-ui/apps/web-antd/src/views/work/myWork/user-info-modal.vue      |   62 +
 ruoyi-ui/apps/web-antd/src/views/work/Issued/info.tsx                 |  129 ++
 27 files changed, 3,198 insertions(+), 20 deletions(-)

diff --git a/ruoyi-ui/apps/web-antd/src/adapter/component/index.ts b/ruoyi-ui/apps/web-antd/src/adapter/component/index.ts
index c61e9b4..f8f783f 100644
--- a/ruoyi-ui/apps/web-antd/src/adapter/component/index.ts
+++ b/ruoyi-ui/apps/web-antd/src/adapter/component/index.ts
@@ -39,7 +39,7 @@
 } 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,
@@ -92,6 +92,7 @@
   | 'DefaultButton'
   | 'Divider'
   | 'FileUpload'
+  | 'NewFileUpload'
   | 'IconPicker'
   | 'ImageUpload'
   | 'Input'
@@ -193,6 +194,7 @@
     Upload,
     ImageUpload,
     FileUpload,
+    NewFileUpload,
     RichTextarea,
   };
 
diff --git a/ruoyi-ui/apps/web-antd/src/components/upload/index.ts b/ruoyi-ui/apps/web-antd/src/components/upload/index.ts
index b86d495..6ca877d 100644
--- a/ruoyi-ui/apps/web-antd/src/components/upload/index.ts
+++ b/ruoyi-ui/apps/web-antd/src/components/upload/index.ts
@@ -1,2 +1,3 @@
 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';
diff --git a/ruoyi-ui/apps/web-antd/src/components/upload/src/new-file-upload.vue b/ruoyi-ui/apps/web-antd/src/components/upload/src/new-file-upload.vue
new file mode 100644
index 0000000..f7debc2
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/components/upload/src/new-file-upload.vue
@@ -0,0 +1,247 @@
+<!-- 杩欐槸涓�涓敤鏉ヨ嚜瀹氫箟鍖栫殑鏂囦欢涓婁紶缁勪欢 -->
+<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绛�(娴嬭瘯鍒ゆ柇涓嶅噯纭�)  涓嶆敮鎸乮mage/*绫讳技鐨勫啓娉�
+     * 闇�鑷鏀归�� ./helper/checkFileType鏂规硶
+     */
+    accept?: string[];
+    api?: (
+      file: Blob | File,
+      onUploadProgress?: AxiosProgressEvent,
+    ) => Promise<AxiosResponse<any>>;
+    disabled?: boolean;
+    helpText?: string;
+    // 鏈�澶ф暟閲忕殑鏂囦欢锛孖nfinity涓嶉檺鍒�
+    maxNumber?: number;
+    // 鏂囦欢鏈�澶у灏慚B
+    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);
+    /**
+     * 鐢眊etValue澶勭悊 浼犲璞¤繃鍘�
+     * 鐩存帴浼爏tring(id)浼氳杞负Number
+     * 鍐呴儴鐨勯�昏緫鐢眗equestClient.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>
diff --git a/ruoyi-ui/apps/web-antd/src/locales/langs/en-US/pages.json b/ruoyi-ui/apps/web-antd/src/locales/langs/en-US/pages.json
index ea66e51..1df7c96 100644
--- a/ruoyi-ui/apps/web-antd/src/locales/langs/en-US/pages.json
+++ b/ruoyi-ui/apps/web-antd/src/locales/langs/en-US/pages.json
@@ -1,7 +1,11 @@
 {
   "common": {
     "add": "Add",
+    "WorkIssued": "WorkIssued",
     "edit": "Edit",
+    "look": "Look",
+    "report":"report",
+    "AssigningWork":"Assigning Work",
     "delete": "Delete",
     "more": "More",
     "search": "Search",
diff --git a/ruoyi-ui/apps/web-antd/src/locales/langs/zh-CN/pages.json b/ruoyi-ui/apps/web-antd/src/locales/langs/zh-CN/pages.json
index 1bff1bb..80bc598 100644
--- a/ruoyi-ui/apps/web-antd/src/locales/langs/zh-CN/pages.json
+++ b/ruoyi-ui/apps/web-antd/src/locales/langs/zh-CN/pages.json
@@ -1,7 +1,11 @@
 {
   "common": {
     "add": "鏂板",
+    "WorkIssued": "宸ヤ綔涓嬪彂",
     "edit": "缂栬緫",
+    "look": "鏌ョ湅",
+    "report":"姹囨姤",
+    "AssigningWork":"宸ヤ綔涓嬪彂",
     "delete": "鍒犻櫎",
     "more": "鏇村",
     "search": "鎼滅储",
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/Issued/data.tsx b/ruoyi-ui/apps/web-antd/src/views/work/Issued/data.tsx
new file mode 100644
index 0000000..53e7c8e
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/Issued/data.tsx
@@ -0,0 +1,233 @@
+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 鈥斺�� 姝e湪涓婁紶
+  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)
+}
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/Issued/dept-tree.vue b/ruoyi-ui/apps/web-antd/src/views/work/Issued/dept-tree.vue
new file mode 100644
index 0000000..df9d6c2
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/Issued/dept-tree.vue
@@ -0,0 +1,128 @@
+<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>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/Issued/index.vue b/ruoyi-ui/apps/web-antd/src/views/work/Issued/index.vue
index d884f8b..9e2834d 100644
--- a/ruoyi-ui/apps/web-antd/src/views/work/Issued/index.vue
+++ b/ruoyi-ui/apps/web-antd/src/views/work/Issued/index.vue
@@ -1,13 +1,295 @@
-<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>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/Issued/info.tsx b/ruoyi-ui/apps/web-antd/src/views/work/Issued/info.tsx
new file mode 100644
index 0000000..cd00bb8
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/Issued/info.tsx
@@ -0,0 +1,129 @@
+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 || '鏃�';
+    },
+  },
+];
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/Issued/user-drawer.vue b/ruoyi-ui/apps/web-antd/src/views/work/Issued/user-drawer.vue
new file mode 100644
index 0000000..0a24976
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/Issued/user-drawer.vue
@@ -0,0 +1,213 @@
+<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>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/Issued/user-import-modal.vue b/ruoyi-ui/apps/web-antd/src/views/work/Issued/user-import-modal.vue
new file mode 100644
index 0000000..fe81465
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/Issued/user-import-modal.vue
@@ -0,0 +1,108 @@
+<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>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/Issued/user-info-modal.vue b/ruoyi-ui/apps/web-antd/src/views/work/Issued/user-info-modal.vue
new file mode 100644
index 0000000..63559af
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/Issued/user-info-modal.vue
@@ -0,0 +1,62 @@
+<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);
+  // 澶栭儴鐨剅oleIds 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>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/Issued/user-reset-pwd-modal.vue b/ruoyi-ui/apps/web-antd/src/views/work/Issued/user-reset-pwd-modal.vue
new file mode 100644
index 0000000..a3acf33
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/Issued/user-reset-pwd-modal.vue
@@ -0,0 +1,111 @@
+<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>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/myWork/data.tsx b/ruoyi-ui/apps/web-antd/src/views/work/myWork/data.tsx
new file mode 100644
index 0000000..b0b9fd5
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/myWork/data.tsx
@@ -0,0 +1,262 @@
+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,
+};
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/myWork/dept-tree.vue b/ruoyi-ui/apps/web-antd/src/views/work/myWork/dept-tree.vue
new file mode 100644
index 0000000..df9d6c2
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/myWork/dept-tree.vue
@@ -0,0 +1,128 @@
+<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>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/myWork/index.vue b/ruoyi-ui/apps/web-antd/src/views/work/myWork/index.vue
index 7dabebf..149cfd5 100644
--- a/ruoyi-ui/apps/web-antd/src/views/work/myWork/index.vue
+++ b/ruoyi-ui/apps/web-antd/src/views/work/myWork/index.vue
@@ -1,13 +1,298 @@
-<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>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/myWork/info.tsx b/ruoyi-ui/apps/web-antd/src/views/work/myWork/info.tsx
new file mode 100644
index 0000000..cd00bb8
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/myWork/info.tsx
@@ -0,0 +1,129 @@
+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 || '鏃�';
+    },
+  },
+];
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-assign-work.vue b/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-assign-work.vue
new file mode 100644
index 0000000..a394a80
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-assign-work.vue
@@ -0,0 +1,170 @@
+<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>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-drawer.vue b/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-drawer.vue
new file mode 100644
index 0000000..94d90a6
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-drawer.vue
@@ -0,0 +1,218 @@
+<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>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-import-modal.vue b/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-import-modal.vue
new file mode 100644
index 0000000..fe81465
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-import-modal.vue
@@ -0,0 +1,108 @@
+<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>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-info-modal.vue b/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-info-modal.vue
new file mode 100644
index 0000000..63559af
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-info-modal.vue
@@ -0,0 +1,62 @@
+<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);
+  // 澶栭儴鐨剅oleIds 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>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-report.vue b/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-report.vue
new file mode 100644
index 0000000..93a20f6
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-report.vue
@@ -0,0 +1,176 @@
+<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>
diff --git a/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-reset-pwd-modal.vue b/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-reset-pwd-modal.vue
new file mode 100644
index 0000000..a3acf33
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/work/myWork/user-reset-pwd-modal.vue
@@ -0,0 +1,111 @@
+<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>
diff --git a/ruoyi-ui/packages/@core/base/shared/src/constants/dict-enum.ts b/ruoyi-ui/packages/@core/base/shared/src/constants/dict-enum.ts
index 16de04b..de6b4dd 100644
--- a/ruoyi-ui/packages/@core/base/shared/src/constants/dict-enum.ts
+++ b/ruoyi-ui/packages/@core/base/shared/src/constants/dict-enum.ts
@@ -15,4 +15,7 @@
   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' //骞村害
 }
diff --git a/ruoyi-ui/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue b/ruoyi-ui/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue
index 61852aa..ced202a 100644
--- a/ruoyi-ui/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue
+++ b/ruoyi-ui/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue
@@ -366,7 +366,7 @@
         </div>
 
         <Transition name="slide-up">
-          <FormMessage class="absolute bottom-1" />
+          <FormMessage class="absolute" />
         </Transition>
       </div>
     </FormItem>
diff --git a/ruoyi-ui/packages/locales/src/langs/en-US/common.json b/ruoyi-ui/packages/locales/src/langs/en-US/common.json
index 440af82..67fc59d 100644
--- a/ruoyi-ui/packages/locales/src/langs/en-US/common.json
+++ b/ruoyi-ui/packages/locales/src/langs/en-US/common.json
@@ -15,6 +15,7 @@
   "enabled": "Enabled",
   "disabled": "Disabled",
   "edit": "Edit",
+  "look": "Look",
   "delete": "Delete",
   "create": "Create",
   "yes": "Yes",
diff --git a/ruoyi-ui/packages/locales/src/langs/zh-CN/common.json b/ruoyi-ui/packages/locales/src/langs/zh-CN/common.json
index 95ec5f7..800af26 100644
--- a/ruoyi-ui/packages/locales/src/langs/zh-CN/common.json
+++ b/ruoyi-ui/packages/locales/src/langs/zh-CN/common.json
@@ -15,6 +15,7 @@
   "enabled": "宸插惎鐢�",
   "disabled": "宸茬鐢�",
   "edit": "淇敼",
+  "look": "鏌ョ湅",
   "delete": "鍒犻櫎",
   "create": "鏂板",
   "yes": "鏄�",

--
Gitblit v1.9.3