소스 검색

feat: 新增角色分配用户功能,优化上传和报表组件

1. 新增角色分配用户弹窗组件,支持给角色绑定/解绑用户
2. 为角色列表页新增分配用户操作按钮与权限控制
3. 优化上传组件的图片压缩与文件大小校验逻辑
4. 优化动态报表组件,过滤试用版警告sheet与Illustration字段解析
5. 修复上传组件弹窗显示与任务订单详情弹窗传参问题
xuzhancheng 1 일 전
부모
커밋
09e0bc41c8

+ 11 - 9
yudao-ui-admin-vue3/src/components/DynamicReport/CustomUploadFile.vue

@@ -307,27 +307,29 @@ const beforeUpload = async (file) => {
     ElMessage.error('只能上传' + realAccept.value)
     return false
   }
-  
+
+  // 需要进行大小校验的文件(压缩后或原始文件)
+  let fileToCheck = file
+
   // 如果是图片且启用了压缩,先进行压缩处理
   if (props.enableImageCompression && isImage(file)) {
     try {
-      const compressedFile = await compressImage(file)
-      // 用压缩后的文件替换原文件
-      Object.assign(file, compressedFile)
+      fileToCheck = await compressImage(file)
       ElMessage.success('图片已自动压缩')
     } catch (error) {
       console.error('图片压缩失败:', error)
       ElMessage.warning('图片压缩失败,将使用原图上传')
     }
   }
-  
-  // 校验文件大小(压缩后)
-  if (props.maxSize > 0 && file.size > props.maxSize) {
+
+  // 校验文件大小
+  if (props.maxSize > 0 && fileToCheck.size > props.maxSize) {
     ElMessage.error(`文件大小不能超过 ${formatFileSize(props.maxSize)}`)
     return false
   }
-  
-  return true
+
+  // 返回压缩后的文件,Element Plus 会用它替代原始文件上传
+  return fileToCheck
 }
 
 const activeUploads = ref(false)

+ 18 - 18
yudao-ui-admin-vue3/src/components/DynamicReport/SpreadViewer.vue

@@ -265,7 +265,11 @@ const initPreview = async () => {
               if (trimmed.endsWith('.jpg') || trimmed.endsWith('.png')) {
                 // sheetData[i.colCode] = null;
               } else if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
-                try { sheetData[i.colCode] = JSON.parse(val); } catch { sheetData[i.colCode] = val; }
+                if(i.colCode === 'Illustration'){
+                  sheetData[i.colCode] = null
+                }else{
+                  try { sheetData[i.colCode] = JSON.parse(val); } catch { sheetData[i.colCode] = val; }
+                }
               } else if (val == 'false' || val == 'true') {
                 sheetData[i.colCode] = val == 'true' ? 'true' : null;
               } else {
@@ -508,31 +512,27 @@ const selectWingdings = (item) => {
 
 const handleSave = () => {
   loading.value = true;
-  console.log(1)
   let dataSource = {};
-  console.log(1)
   previewSpread.sheets.forEach((sheet) => {
-    // 收集 sheet 的数据源(兼容 table 绑定字段)
-    console.log(1)
-    const sheetData = collectSheetDataSource(sheet);
-    console.log(sheetData)
-    for (const key in sheetData) {
-      console.log(1)
-      if (dataSource[key] && sheetData[key] == sheetData[key]) {
-      } else {
-        dataSource[key] = sheetData[key];
+    if (sheet.name() && sheet.name() !== '试用版警告') {
+      // 收集 sheet 的数据源(兼容 table 绑定字段)
+      const sheetData = collectSheetDataSource(sheet);
+      console.log(sheetData)
+      for (const key in sheetData) {
+        if (dataSource[key] && sheetData[key] == sheetData[key]) {
+        } else {
+          dataSource[key] = sheetData[key];
+        }
       }
+      // 收集形状(浮动图片)数据
+      collectShapesIntoDataSource(sheet, dataSource);
     }
-    console.log(1)
-    // 收集形状(浮动图片)数据
-    collectShapesIntoDataSource(sheet, dataSource);
   });
- console.log(dataSource)
   if (Object.keys(dataSource).length > 0) {
-     console.log(1)
     // 序列化为后端存储格式(对象/数组 → JSON 字符串)
     const serializedData = serializeDataSourceForStorage(dataSource);
- console.log(1)
+    console.log(serializedData);
+    
     DynamicTbValApi.saveAllColValue(insId.value, serializedData).then(res => {
       if (res) {
         ElMessage.success('保存成功')

+ 1 - 0
yudao-ui-admin-vue3/src/views/pressure2/dynamictb/index.vue

@@ -272,6 +272,7 @@
           :auto-upload="false"
           :limit="1"
           accept=".zip"
+          v-if="importDialogVisible"
           :on-change="handleFileChange"
           :on-remove="handleFileRemove"
           drag

+ 2 - 0
yudao-ui-admin-vue3/src/views/pressure2/pipetaskorder/components/TaskOrderDetailDialog.vue

@@ -894,6 +894,8 @@
     v-if="orderReportVisible"
     v-model:visible="orderReportVisible"
     :orderId="props.taskOrder.id"
+    :serviceFormReceiver="taskOrderDetail.unitContact"
+    :serviceFormReceiverPhone="taskOrderDetail.unitPhone"
     type="pipe"
   />
   <ServiceRecordList

+ 174 - 0
yudao-ui-admin-vue3/src/views/system/role/RoleAssignUserForm.vue

@@ -0,0 +1,174 @@
+<template>
+  <Dialog v-model="dialogVisible" title="分配用户">
+    <el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px">
+      <el-form-item label="角色名称">
+        <el-tag>{{ formData.name }}</el-tag>
+      </el-form-item>
+      <el-form-item label="角色标识">
+        <el-tag>{{ formData.code }}</el-tag>
+      </el-form-item>
+      <el-form-item label="选择用户">
+        <el-input
+          v-model="userSearch.nickName"
+          clearable
+          placeholder="请输入用户名搜索"
+          class="mb-14px"
+          @keyup.enter="fetchUserList"
+        >
+          <template #append>
+            <el-button @click="fetchUserList">
+              <Icon icon="ep:search" />
+            </el-button>
+          </template>
+        </el-input>
+        <SmartTable
+          ref="userTableRef"
+          v-model:pageNo="userSearch.pageNo"
+          v-model:pagesize="userSearch.pageSize"
+          :total="userSearch.total"
+          :columns="userColumns"
+          :data="userTableData"
+          :buttons="[]"
+          :showSettingTools="false"
+          :showSearch="false"
+          :showRefresh="false"
+          @on-page-size-change="onPageSizeChange"
+          @on-page-no-change="onPageNoChange"
+        />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script lang="ts" setup>
+import * as RoleApi from '@/api/system/role'
+import * as UserApi from '@/api/system/user'
+import SmartTable from '@/components/SmartTable/SmartTable'
+
+defineOptions({ name: 'SystemRoleAssignUserForm' })
+
+const message = useMessage()
+
+const dialogVisible = ref(false)
+const formLoading = ref(false)
+const formData = reactive({
+  roleId: undefined as number | undefined,
+  name: '',
+  code: ''
+})
+const formRef = ref()
+
+// 用户搜索条件
+const userSearch = reactive({
+  nickName: '',
+  pageNo: 1,
+  pageSize: 10,
+  total: 0
+})
+
+// 用户表格
+const userTableRef = ref()
+const userTableData = ref<any[]>([])
+const userColumns = [
+  { type: 'selection', width: '50px' },
+  { label: '工号', prop: 'employeeNo' },
+  { label: '用户名', prop: 'nickname' },
+  { label: '部门', prop: 'deptName' }
+]
+
+/** 获取用户列表 */
+const fetchUserList = async () => {
+  const params: PageParam = {
+    pageNo: userSearch.pageNo,
+    pageSize: userSearch.pageSize,
+    nickName: userSearch.nickName || undefined
+  }
+  const data = await UserApi.getUserPage(params)
+  userTableData.value = data.list || []
+  userSearch.total = data.total || 0
+
+  // 预选已有该角色的用户
+  await nextTick()
+  preSelectRows()
+}
+
+/** 根据角色预选已有的用户 */
+const preSelectRows = async () => {
+  if (!formData.roleId) return
+  const existingUserIds = await RoleApi.getRoleUserIdList(formData.roleId)
+  if (!existingUserIds || existingUserIds.length === 0) return
+  const tableRef = userTableRef.value?.getTableRef?.()
+  if (!tableRef) return
+  userTableData.value.forEach((row: any) => {
+    if (existingUserIds.includes(String(row.id))) {
+      tableRef.toggleRowSelection(row, true)
+    }
+  })
+}
+
+const onPageSizeChange = (val: number) => {
+  userSearch.pageSize = val
+  fetchUserList()
+}
+const onPageNoChange = (val: number) => {
+  userSearch.pageNo = val
+  fetchUserList()
+}
+
+/** 打开弹窗 */
+const open = async (row: RoleApi.RoleVO) => {
+  dialogVisible.value = true
+  resetForm()
+  formData.roleId = row.id
+  formData.name = row.name
+  formData.code = row.code
+  formLoading.value = true
+  try {
+    await fetchUserList()
+  } finally {
+    formLoading.value = false
+  }
+}
+defineExpose({ open })
+
+/** 提交表单 */
+const emit = defineEmits(['success'])
+const submitForm = async () => {
+  if (!formData.roleId) {
+    message.error('角色信息异常')
+    return
+  }
+  formLoading.value = true
+  try {
+    const tableRef = userTableRef.value?.getTableRef?.()
+    // 获取所有选中的行(跨页选中)
+    const selectedRows = tableRef?.getSelectionRows?.() || []
+    const selectedUserIds: string[] = selectedRows.map((row: any) => String(row.id))
+    if (selectedUserIds.length === 0) {
+      message.warning('请至少选择一个用户')
+      return
+    }
+    await RoleApi.assignRoleUsers({
+      roleId: String(formData.roleId),
+      userIds: selectedUserIds
+    })
+    message.success('分配用户成功')
+    dialogVisible.value = false
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置 */
+const resetForm = () => {
+  userSearch.nickName = ''
+  userSearch.pageNo = 1
+  userSearch.pageSize = 10
+  userSearch.total = 0
+  userTableData.value = []
+}
+</script>

+ 19 - 0
yudao-ui-admin-vue3/src/views/system/role/index.vue

@@ -137,6 +137,16 @@
           >
             数据权限
           </el-button>
+          <el-button
+            v-hasPermi="['system:permission:assign-role-user']"
+            link
+            preIcon="ep:user"
+            title="分配用户"
+            type="primary"
+            @click="openAssignUserForm(scope.row)"
+          >
+            分配用户
+          </el-button>
           <el-button
             v-hasPermi="['system:role:delete']"
             link
@@ -163,6 +173,8 @@
   <RoleAssignMenuForm ref="assignMenuFormRef" @success="getList" />
   <!-- 表单弹窗:数据权限 -->
   <RoleDataPermissionForm ref="dataPermissionFormRef" @success="getList" />
+  <!-- 表单弹窗:分配用户 -->
+  <RoleAssignUserForm ref="assignUserFormRef" @success="getList" />
 </template>
 <script lang="ts" setup>
 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
@@ -172,6 +184,7 @@ import * as RoleApi from '@/api/system/role'
 import RoleForm from './RoleForm.vue'
 import RoleAssignMenuForm from './RoleAssignMenuForm.vue'
 import RoleDataPermissionForm from './RoleDataPermissionForm.vue'
+import RoleAssignUserForm from './RoleAssignUserForm.vue'
 
 defineOptions({ name: 'SystemRole' })
 
@@ -234,6 +247,12 @@ const openAssignMenuForm = async (row: RoleApi.RoleVO) => {
   assignMenuFormRef.value.open(row)
 }
 
+/** 分配用户操作 */
+const assignUserFormRef = ref()
+const openAssignUserForm = async (row: RoleApi.RoleVO) => {
+  assignUserFormRef.value.open(row)
+}
+
 /** 删除按钮操作 */
 const handleDelete = async (id: number) => {
   try {