Selaa lähdekoodia

feat: ✨ 接口联调

zhangtao 17 tuntia sitten
vanhempi
commit
b9f6acc50e

+ 4 - 0
src/api/apiDefinitions.ts

@@ -7,4 +7,8 @@ export default {
   "sys.updateStatus": ["PUT", "/staff/staff/updateStatus"],
   "sys.findByDeptTree": ["GET", "/sys/sysDepart/findByDepTree"],
   "sys.rolelist": ["GET", "/sys/role/list"],
+  "sys.userAdd": ["POST", "/staff/staff/add"],
+  "sys.userEdit": ["POST", "/staff/staff/edit"],
+  "sys.userDel": ["delete", "/staff/staff/delete"],
+  "sys.queryById": ["GET", "/staff/staff/queryById"],
 };

+ 53 - 44
src/api/core/handlers.ts

@@ -31,54 +31,57 @@ export async function handleAlovaResponse(
     | UniApp.UploadFileSuccessCallbackResult
     | UniApp.DownloadSuccessData,
 ) {
-  const globalToast = useGlobalToast();
-  // Extract status code and data from UniApp response
-  const { statusCode, data } =
-    response as UniNamespace.RequestSuccessCallbackResult;
+  return new Promise((resolve, reject) => {
+    const globalToast = useGlobalToast();
+    // Extract status code and data from UniApp response
+    const { statusCode, data } =
+      response as UniNamespace.RequestSuccessCallbackResult;
 
-  // 处理401/403错误(如果不是在handleAlovaResponse中处理的)
-  if (
-    typeof data === "object" &&
-    data !== null &&
-    "code" in data &&
-    (data.code === 401 || data.code === 403)
-  ) {
-    // 如果是未授权错误,清除用户信息并跳转到登录页
-    uni.showToast({
-      title: "登录已过期,请重新登录!",
-      duration: 2000,
-      icon: "none",
-    });
-    const timer = setTimeout(() => {
-      clearTimeout(timer);
-      router.replaceAll({ name: "login" });
-    }, 500);
+    // 处理401/403错误(如果不是在handleAlovaResponse中处理的)
+    if (
+      typeof data === "object" &&
+      data !== null &&
+      "code" in data &&
+      (data.code === 401 || data.code === 403)
+    ) {
+      // 如果是未授权错误,清除用户信息并跳转到登录页
+      uni.showToast({
+        title: "登录已过期,请重新登录!",
+        duration: 2000,
+        icon: "none",
+      });
+      const timer = setTimeout(() => {
+        clearTimeout(timer);
+        router.replaceAll({ name: "login" });
+      }, 500);
 
-    throw new ApiError("登录已过期,请重新登录!", statusCode, data);
-  }
+      throw new ApiError("登录已过期,请重新登录!", statusCode, data);
+    }
 
-  // Handle HTTP error status codes
-  if (
-    typeof data === "object" &&
-    data !== null &&
-    "code" in data &&
-    data.code >= 400
-  ) {
-    uni.showToast({
-      title: (data as any).message,
-      duration: 2000,
-      icon: "none",
-    });
-  }
+    // Handle HTTP error status codes
+    if (
+      typeof data === "object" &&
+      data !== null &&
+      "code" in data &&
+      data.code >= 400
+    ) {
+      uni.showToast({
+        title: (data as any).message,
+        duration: 2000,
+        icon: "none",
+      });
+      reject(`${(data as any).message}`);
+    }
 
-  // The data is already parsed by UniApp adapter
-  const json = data as ApiResponse;
-  // Log response in development
-  if (import.meta.env.MODE === "development") {
-    console.log("[Alova Response]", json);
-  }
-  // Return data for successful responses
-  return json.result;
+    // The data is already parsed by UniApp adapter
+    const json = data as ApiResponse;
+    // Log response in development
+    if (import.meta.env.MODE === "development") {
+      console.log("[Alova Response]", json);
+    }
+    // Return data for successful responses
+    resolve(json.result);
+  });
 }
 
 // Handle request errors
@@ -88,6 +91,12 @@ export function handleAlovaError(error: any, method: Method) {
   if (import.meta.env.MODE === "development") {
     console.error("[Alova Error]", error, method);
   }
+  if (error.name === "Error") {
+    uni.showToast({
+      title: "服务器异常,请稍后再试",
+      icon: "none",
+    });
+  }
 
   // 处理401/403错误(如果不是在handleAlovaResponse中处理的)
   if (error instanceof ApiError && (error.code === 401 || error.code === 403)) {

+ 2 - 3
src/api/core/instance.ts

@@ -28,8 +28,8 @@ export const alovaInstance = createAlova({
         `[Alova Request] ${method.type} ${method.url}`,
         method.data || method.config.params,
       );
-      console.log(`[API Base URL] ${import.meta.env.VITE_API_BASE_URL}`);
-      console.log(`[Environment] ${import.meta.env.VITE_ENV_NAME}`);
+      // console.log(`[API Base URL] ${import.meta.env.VITE_API_BASE_URL}`);
+      // console.log(`[Environment] ${import.meta.env.VITE_ENV_NAME}`);
     }
   },
 
@@ -46,7 +46,6 @@ export const alovaInstance = createAlova({
       // Any cleanup or logging can be done here
     },
   },
-
   // We'll use the middleware in the hooks
   // middleware is not directly supported in createAlova options
 

+ 1 - 0
src/components.d.ts

@@ -15,6 +15,7 @@ declare module 'vue' {
     WdBadge: typeof import('wot-design-uni/components/wd-badge/wd-badge.vue')['default']
     WdButton: typeof import('wot-design-uni/components/wd-button/wd-button.vue')['default']
     WdCard: typeof import('wot-design-uni/components/wd-card/wd-card.vue')['default']
+    WdCell: typeof import('wot-design-uni/components/wd-cell/wd-cell.vue')['default']
     WdCheckbox: typeof import('wot-design-uni/components/wd-checkbox/wd-checkbox.vue')['default']
     WdCheckboxGroup: typeof import('wot-design-uni/components/wd-checkbox-group/wd-checkbox-group.vue')['default']
     WdConfigProvider: typeof import('wot-design-uni/components/wd-config-provider/wd-config-provider.vue')['default']

+ 4 - 2
src/config/index.ts

@@ -2,9 +2,9 @@ const mapEnvVersion = {
   /**
    * 	开发版
    */
-  develop: "http://192.168.1.166:8080/jeecg-boot",
+  // develop: "http://192.168.1.166:8080/jeecg-boot",
   // develop: "http://192.168.1.34:8080/jeecg-boot",
-  // develop: "http://192.168.0.11:8080/jeecg-boot",
+  develop: "http://192.168.0.11:8080/jeecg-boot",
   /**
    * 	体验版
    */
@@ -24,3 +24,5 @@ export const BASE_URL =
 // export const BASE_UPLOADURL = "http://192.168.0.11:8080/jeecg-boot/upload";
 export const BASE_UPLOADURL =
   "http://192.168.1.166:8080/jeecg-boot/sys/common/upload";
+// export const BASE_UPLOADURL =
+//   "http://192.168.0.11:8080/jeecg-boot/sys/common/upload";

+ 6 - 4
src/pages/login/index.vue

@@ -75,8 +75,8 @@ const {
 ).onError((error) => {});
 
 const modelForm = reactive({
-  username: "测试张涛",
-  password: "Qwer123456.",
+  username: "mdadmin",
+  password: "admin123.",
 });
 function handleClickLeft() {
   uni.navigateBack();
@@ -89,9 +89,11 @@ async function handleLogin() {
     });
   }
   const res = await performLogin(modelForm.username, modelForm.password);
-  useUserStore().handleLogin(res.result.userInfo, res.result.token);
+  useUserStore().handleLogin(res.userInfo, res.token);
+  console.log(res, "登录信息");
+
   uni.showToast({
-    title: res.message,
+    title: "登录成功!",
     icon: "none",
     duration: 2000,
     mask: true,

+ 61 - 19
src/subPack/EmployeeList/index.vue

@@ -1,7 +1,7 @@
 <template>
   <view class="px32rpx py-20rpx">
     <!-- <view class="bg-white p24rpx rounded-32rpx box-border"> </view> -->
-    <wd-card type="rectangle" v-for="item in data">
+    <wd-card type="rectangle" v-for="item in data" :key="item.id">
       <template #title>
         <view class="flex items-center">
           <view class="font-semibold text-32rpx"> {{ item.realname }}</view>
@@ -27,11 +27,13 @@
           />
           <view class="flex items-center">
             <view class="mr20rpx">
-              <commonbtn bg-color="rgba(255,88,105,0.2)"
+              <commonbtn
+                bg-color="rgba(255,88,105,0.2)"
+                @click="handleDelete(item)"
                 ><text class="text-#FF5869">删除</text>
               </commonbtn>
             </view>
-            <commonbtn bg-color="rgba(0,116,255,0.2)"
+            <commonbtn bg-color="rgba(0,116,255,0.2)" @click="handleEdit(item)"
               ><text class="text-#0074FF">修改</text>
             </commonbtn>
           </view>
@@ -40,31 +42,46 @@
     </wd-card>
     <view class="h150rpx"></view>
   </view>
-  <fixdbtn block size="large" @click="handleAdd">新增员工</fixdbtn>
+  <fixdbtn block size="large" @click="handleAdd(0, '')">新增员工</fixdbtn>
 </template>
 
 <script setup lang="ts">
 import { createGlobalLoadingMiddleware } from "@/api/core/middleware";
 import { type SwitchBeforeChangeOption } from "wot-design-uni/components/wd-switch/types";
+import { useMessage } from "wot-design-uni";
+const msg = useMessage();
 const statusText = ["关闭", "正常"];
-function handleAdd() {
+function handleAdd(type: number, id: string) {
   uni.navigateTo({
-    url: "/subPack/EmployeeListAdd/index?type=0",
+    url: `/subPack/EmployeeListAdd/index?type=${type}&id=${id}`,
   });
 }
-const { loading, data, page, pageSize, pageCount, total, refresh } =
-  usePagination(
-    (page, pageSize) =>
-      Apis.sys.staff({
-        data: { page, pageSize },
-      }),
-    {
-      middleware: createGlobalLoadingMiddleware(),
-      data: (resp) => resp.records,
-      initialPage: 1, // 初始页码,默认为1
-      initialPageSize: 10, // 初始每页数据条数,默认为10
-    },
-  ).onError((error) => {});
+async function getData() {
+  await getList();
+}
+onShow(() => getData());
+const {
+  loading,
+  data,
+  page,
+  pageSize,
+  pageCount,
+  total,
+  refresh,
+  send: getList,
+} = usePagination(
+  (page, pageSize) =>
+    Apis.sys.staff({
+      data: { page, pageSize },
+    }),
+  {
+    middleware: createGlobalLoadingMiddleware(),
+    data: (resp) => resp.records,
+    initialPage: 1, // 初始页码,默认为1
+    initialPageSize: 10, // 初始每页数据条数,默认为10
+    immediate: false,
+  },
+).onError((error) => {});
 const { send: uploadStatus } = useRequest(
   (status, id) =>
     Apis.sys.updateStatus({
@@ -76,10 +93,35 @@ const { send: uploadStatus } = useRequest(
     immediate: false,
   },
 ).onError((error) => {});
+const { send: del } = useRequest(
+  (id) =>
+    Apis.sys.userDel({
+      params: { id },
+    }),
+
+  {
+    middleware: createGlobalLoadingMiddleware(),
+    immediate: false,
+  },
+).onError((error) => {});
 async function handleChange(e: SwitchBeforeChangeOption, id: string) {
   await uploadStatus(e.value, id);
   refresh();
 }
+async function handleDelete(item: { realname: string; id: string }) {
+  msg
+    .confirm({
+      title: `确定删除${item.realname}这个人吗?,删除后不可恢复`,
+      closeOnClickModal: false,
+    })
+    .then(async () => {
+      await del(item.id);
+      refresh();
+    });
+}
+function handleEdit(item: { id: string }) {
+  handleAdd(1, item.id);
+}
 </script>
 
 <style scoped>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 945 - 428
src/subPack/EmployeeListAdd/components/tree/index.vue


+ 197 - 0
src/subPack/EmployeeListAdd/components/tree/props.ts

@@ -0,0 +1,197 @@
+export default {
+  /**
+   * 树的数据
+   */
+  data: {
+    type: Array,
+    default: () => [],
+  },
+  /**
+   * 主题色
+   */
+  themeColor: {
+    type: String,
+    default: '#007aff',
+  },
+  /**
+   * 是否开启多选,默认单选
+   */
+  showCheckbox: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 默认选中的节点,注意单选时为单个key,多选时为key的数组
+   */
+  defaultCheckedKeys: {
+    type: [Array, String, Number],
+    default: null,
+  },
+  /**
+   * 是否默认展开全部
+   */
+  defaultExpandAll: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 默认展开的节点
+   */
+  defaultExpandedKeys: {
+    type: Array,
+    default: null,
+  },
+  /**
+   * 筛选关键词
+   */
+  filterValue: {
+    type: String,
+    default: '',
+  },
+  /**
+   * 是否自动展开到选中的节点,默认不展开
+   */
+  expandChecked: {
+    type: Boolean,
+    default: false,
+  },
+
+  /**
+   * (旧)字段对应内容,默认为 {label: 'label',key: 'key', children: 'children', disabled: 'disabled', append: 'append'}
+   * 注意:1.5.0版本后不再兼容
+   */
+  field: {
+    type: Object,
+    default: null,
+  },
+  /**
+   * 标签字段(新,拆分了)
+   */
+  labelField: {
+    type: String,
+    default: 'label',
+  },
+  /**
+   * 值字段(新,拆分了)
+   */
+  valueField: {
+    type: String,
+    default: 'value',
+  },
+  /**
+   * 下级字段(新,拆分了)
+   */
+  childrenField: {
+    type: String,
+    default: 'children',
+  },
+  /**
+   * 禁用字段(新,拆分了)
+   */
+  disabledField: {
+    type: String,
+    default: 'disabled',
+  },
+  /**
+   * 末级节点字段(新,拆分了)
+   */
+  leafField: {
+    type: String,
+    default: 'leaf',
+  },
+  /**
+   * 副标签字段(新,拆分了)
+   */
+  appendField: {
+    type: String,
+    default: 'append',
+  },
+  /**
+   * 排序字段(新,拆分了)
+   */
+  sortField: {
+    type: String,
+    default: 'sort',
+  },
+  /**
+   * Api数据返回后的结果路径,支持嵌套如`data.list`
+   */
+  resultField: {
+    type: String,
+    default: '',
+  },
+  isLeafFn: {
+    type: Function,
+    default: null,
+  },
+  /**
+   * 是否显示单选图标,默认显示
+   */
+  showRadioIcon: {
+    type: Boolean,
+    default: true,
+  },
+  /**
+   * 单选时只允许选中末级,默认可随意选中
+   */
+  onlyRadioLeaf: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 多选时,是否执行父子不关联的任意勾选,默认父子关联
+   */
+  checkStrictly: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 为 true 时,空的 children 数组会显示展开图标
+   */
+  loadMode: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 异步加载接口
+   */
+  loadApi: {
+    type: Function,
+    default: null,
+  },
+  /**
+   * 是否总在首次的时候加载一下内容,来比对是否一致
+   */
+  alwaysFirstLoad: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 是否渲染(操作)禁用值
+   */
+  checkedDisabled: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 是否返回已禁用的但已选中的key
+   */
+  packDisabledkey: {
+    type: Boolean,
+    default: true,
+  },
+  /**
+   * 选择框的位置,可选 left/right
+   */
+  checkboxPlacement: {
+    type: String,
+    default: 'left',
+  },
+  /**
+   * 子项缩进距离,默认40,单位rpx
+   */
+  indent: {
+    type: Number,
+    default: 40,
+  },
+}

+ 151 - 0
src/subPack/EmployeeListAdd/components/tree/utils.ts

@@ -0,0 +1,151 @@
+/** 未选 */
+export const unCheckedStatus = 0;
+/** 半选 */
+export const halfCheckedStatus = 1;
+/** 选中 */
+export const isCheckedStatus = 2;
+
+/**
+ * 深拷贝内容
+ * @param originData 拷贝对象
+ * @author crlang(https://crlang.com)
+ */
+export function deepClone(originData) {
+  const type = Object.prototype.toString.call(originData);
+  let data;
+  if (type === "[object Array]") {
+    data = [];
+    for (let i = 0; i < originData.length; i++) {
+      data.push(deepClone(originData[i]));
+    }
+  } else if (type === "[object Object]") {
+    data = {};
+    for (const prop in originData) {
+      // eslint-disable-next-line no-prototype-builtins
+      if (originData.hasOwnProperty(prop)) {
+        // 非继承属性
+        data[prop] = deepClone(originData[prop]);
+      }
+    }
+  } else {
+    data = originData;
+  }
+  return data;
+}
+
+/**
+ * 获取所有指定的节点
+ * @param type
+ * @param value
+ * @author crlang(https://crlang.com)
+ */
+export function getAllNodes(list, type, value, packDisabledkey = true) {
+  if (!list || list.length === 0) {
+    return [];
+  }
+
+  const res = [];
+  for (let i = 0; i < list.length; i++) {
+    const item = list[i];
+    if (item[type] === value) {
+      if ((packDisabledkey && item.disabled) || !item.disabled) {
+        res.push(item);
+      }
+    }
+  }
+
+  return res;
+}
+
+/**
+ * 获取所有指定的key值
+ * @param type
+ * @param value
+ * @author crlang(https://crlang.com)
+ */
+export function getAllNodeKeys(list, type, value, packDisabledkey = true) {
+  if (!list || list.length === 0) {
+    return null;
+  }
+
+  const res = [];
+  for (let i = 0; i < list.length; i++) {
+    const item = list[i];
+    if (item[type] === value) {
+      if ((packDisabledkey && item.disabled) || !item.disabled) {
+        res.push(item.key);
+      }
+    }
+  }
+
+  return res.length ? res : null;
+}
+
+/**
+ * 错误输出
+ *
+ * @param msg
+ */
+export function logError(msg, ...args) {
+  console.error(`DaTree: ${msg}`, ...args);
+}
+
+const toString = Object.prototype.toString;
+
+export function is(val, type) {
+  return toString.call(val) === `[object ${type}]`;
+}
+
+/**
+ * 是否对象(Object)
+ * @param val
+
+ */
+export function isObject(val) {
+  return val !== null && is(val, "Object");
+}
+
+/**
+ * 是否数字(Number)
+ * @param val
+
+ */
+export function isNumber(val) {
+  return is(val, "Number");
+}
+
+/**
+ * 是否字符串(String)
+ * @param val
+
+ */
+export function isString(val) {
+  return is(val, "String");
+}
+
+/**
+ * 是否函数方法(Function)
+ * @param val
+
+ */
+export function isFunction(val) {
+  return typeof val === "function";
+}
+
+/**
+ * 是否布尔(Boolean)
+ * @param val
+
+ */
+export function isBoolean(val) {
+  return is(val, "Boolean");
+}
+
+/**
+ * 是否数组(Array)
+ * @param val
+
+ */
+export function isArray(val) {
+  return val && Array.isArray(val);
+}

+ 7 - 2
src/subPack/EmployeeListAdd/components/upload/index.vue

@@ -15,7 +15,7 @@
 </template>
 
 <script setup lang="ts">
-import { BASE_UPLOADURL } from "@/config";
+import { BASE_UPLOADURL, BASE_URL } from "@/config";
 import {
   uploadProps,
   type UploadOversizeEvent,
@@ -40,7 +40,12 @@ function handleOversize(e: UploadOversizeEvent) {
 }
 function handleSuccess(e: UploadSuccessEvent) {
   const { file, fileList, formData } = e;
-  console.log(file, fileList, formData);
+  const fileRes = JSON.parse(String(file.response));
+  props.fileList.push({
+    url: `${BASE_URL}/sys/common/static/${fileRes.message}`,
+    status: "success",
+  });
+  console.log(file);
 }
 </script>
 

+ 302 - 60
src/subPack/EmployeeListAdd/index.vue

@@ -8,7 +8,7 @@
     leftArrow
     @click-left="handleClickLeft"
   ></wd-navbar>
-  <view class="py20rpx">
+  <view class="py20rpx from">
     <wd-form ref="form" :model="formData">
       <view class="bg-white px32rpx py28rpx">
         <customFormItem label="用户名称" required>
@@ -17,7 +17,8 @@
             placeholder="请输入用户名称"
             no-border
             custom-class="inputClass"
-            v-model:model-value="formData.username"
+            prop="realname"
+            v-model:model-value="formData.realname"
             :rules="[{ required: true, message: '请输入用户名称' }]"
           />
         </customFormItem>
@@ -28,6 +29,8 @@
               placeholder="请选择归属部门"
               no-border
               custom-class="inputClass"
+              v-model:model-value="formData.deptName"
+              prop="deptName"
               readonly
               :rules="[{ required: true, message: '请选择归属部门' }]"
             >
@@ -46,6 +49,8 @@
             type="text"
             placeholder="请输入登录账号"
             no-border
+            v-model:model-value="formData.username"
+            prop="username"
             custom-class="inputClass"
             :rules="[{ required: true, message: '请输入登录账号' }]"
           />
@@ -55,24 +60,40 @@
             show-password
             placeholder="请输入登录密码"
             no-border
+            prop="password"
+            v-model:model-value="formData.password"
             :rules="[{ required: true, message: '请输入登录密码' }]"
             custom-class="inputClass"
           />
         </customFormItem>
-        <customFormItem label="角色" required bg="#fff" v-if="rolelist">
-          <wd-radio-group
-            inline
-            v-model:model-value="formData.roleId"
-            shape="dot"
+        <customFormItem
+          label="角色"
+          required
+          bg="#fff"
+          v-if="rolelist"
+          class="customFormItem"
+        >
+          <wd-cell
+            vertical
             :rules="[{ required: true, message: '请选择角色' }]"
+            prop="selectedroles"
           >
-            <wd-radio
-              :value="item.id"
-              v-for="item in rolelist.records"
-              :key="item.id"
-              >{{ item.roleName }}</wd-radio
+            <wd-radio-group
+              inline
+              v-model:model-value="formData.selectedroles"
+              shape="dot"
+              :rules="[{ required: true, message: '请选择角色' }]"
+              @change="handleChangeRole"
+              prop="selectedroles"
             >
-          </wd-radio-group>
+              <wd-radio
+                :value="item.id"
+                v-for="item in rolelist.records"
+                :key="item.id"
+                >{{ item.roleName }}</wd-radio
+              >
+            </wd-radio-group>
+          </wd-cell>
         </customFormItem>
         <customFormItem label="状态" required bg="#fff">
           <wd-switch
@@ -83,70 +104,160 @@
         </customFormItem>
       </view>
       <view class="bg-white mt3 px32rpx py28rpx">
-        <customFormItem label="培训项目" required bg="#fff" v-if="categoryList">
-          <wd-checkbox-group
-            inline
-            v-model:model-value="formData.trainingPrograms"
-            :rules="[{ required: true, message: '请选择培训项目' }]"
+        <customFormItem
+          label="培训项目"
+          required
+          bg="#fff"
+          v-if="categoryList && isShowInstructor"
+          class="customFormItem"
+        >
+          <wd-cell
+            vertical
+            :rules="[
+              {
+                required: true,
+                message: '请选择培训项目',
+                validator(value, rule) {
+                  return value.length > 0;
+                },
+              },
+            ]"
+            prop="trainingPrograms"
           >
-            <wd-checkbox
-              v-model:model-value="item.id"
-              true-value="sting"
-              false-value="string"
-              v-for="item in categoryList.records"
-              :key="item.id"
-              >{{ item.name }}</wd-checkbox
+            <wd-checkbox-group
+              inline
+              v-model:model-value="formData.trainingPrograms"
             >
-          </wd-checkbox-group>
+              <wd-checkbox
+                v-model:model-value="item.id"
+                true-value="sting"
+                false-value="string"
+                v-for="item in categoryList.records"
+                :key="item.id"
+                >{{ item.name }}</wd-checkbox
+              >
+            </wd-checkbox-group>
+          </wd-cell>
         </customFormItem>
         <customFormItem
           label="无犯罪记录"
+          :required="isShowInstructor"
+          bg="#fff"
+        >
+          <wd-cell
+            vertical
+            :rules="[
+              {
+                required: isShowInstructor,
+                message: '请上传无犯罪记录',
+                validator(value, rule) {
+                  return isShowInstructor ? value.length > 0 : true;
+                },
+              },
+            ]"
+            prop="certificateInnocence"
+          >
+            <upload
+              tip="单张照片,不超过5MB"
+              :max-size="5242880"
+              :sourceType="['camera']"
+              :file-list="formData.certificateInnocence"
+              accept="image"
+              :limit="1"
+            ></upload>
+          </wd-cell>
+        </customFormItem>
+        <customFormItem
+          label="背景图"
           required
           bg="#fff"
-          v-if="categoryList"
+          v-if="isShowInstructor"
         >
-          <upload
-            tip="单张照片,不超过5MB"
-            :max-size="5242880"
-            :sourceType="['camera']"
-            accept="image"
-            :limit="1"
-          ></upload>
+          <wd-cell
+            vertical
+            :rules="[
+              {
+                required: true,
+                message: '请上传背景图',
+                validator(value, rule) {
+                  return value.length > 0;
+                },
+              },
+            ]"
+          >
+            <upload
+              tip="单张照片,不超过5MB"
+              :max-size="5242880"
+              :file-list="formData.backgroundImg"
+              :sourceType="['camera']"
+              accept="image"
+              :limit="1"
+            ></upload>
+          </wd-cell>
+        </customFormItem>
+        <customFormItem label="健康证" :required="isShowInstructor" bg="#fff">
+          <wd-cell
+            vertical
+            :rules="[
+              {
+                required: isShowInstructor,
+                message: '请上传健康证',
+                validator(value, rule) {
+                  return isShowInstructor ? value.length > 0 : true;
+                },
+              },
+            ]"
+            prop="healthy"
+          >
+            <upload
+              tip="单张照片,不超过5MB"
+              :max-size="5242880"
+              :sourceType="['camera']"
+              :file-list="formData.healthy"
+              accept="image"
+              :limit="1"
+            ></upload>
+          </wd-cell>
         </customFormItem>
-        <customFormItem label="教学理念" required>
+        <customFormItem label="教学理念" v-if="isShowInstructor">
           <wd-input
             type="text"
             placeholder="请输入教学理念"
             no-border
             custom-class="inputClass"
             v-model:model-value="formData.teachingPhilosophy"
+            prop="teachingPhilosophy"
             :rules="[{ required: true, message: '请输入教学理念' }]"
           />
         </customFormItem>
-        <customFormItem label="擅长描述" required>
+        <customFormItem label="擅长描述" v-if="isShowInstructor">
           <wd-input
             type="text"
             placeholder="请输入擅长描述"
             no-border
             custom-class="inputClass"
             v-model:model-value="formData.excelMsg"
+            prop="excelMsg"
             :rules="[{ required: true, message: '请输入擅长描述' }]"
           />
         </customFormItem>
-        <customFormItem label="荣誉证书" required bg="#fff">
+        <customFormItem label="荣誉证书" bg="#fff">
           <upload
             tip="最多支持2张图片,单张照片不超过5MB"
             :max-size="5242880"
             :sourceType="['camera']"
             accept="image"
-            :limit="2"
+            :file-list="formData.honor"
+            :limit="9"
           ></upload>
         </customFormItem>
       </view>
     </wd-form>
   </view>
   <view class="h-180rpx"></view>
-  <fixdbtn block size="large" @click="handleSubmit">确认</fixdbtn>
+  <fixdbtn block size="large" @click="handleSubmit" :loading="isloading"
+    >确认</fixdbtn
+  >
   <view class="customPopup">
     <wd-popup
       v-model="treeFlage"
@@ -160,13 +271,13 @@
         <view class="mt28rpx bg-white h-700rpx rounded-32rpx overflow-y-scroll">
           <scroll-view scroll-y>
             <tree
-              :localdata="deptList"
-              valueKey="id"
-              textKey="departName"
-              childrenKey="children"
+              :data="deptList"
+              valueField="id"
+              labelField="departName"
+              childrenField="children"
               ref="treePicker"
-              v-model="deptId"
-              @select-change="handleChangeDept"
+              showCheckbox
+              expandChecked
             ></tree>
           </scroll-view>
         </view>
@@ -192,50 +303,164 @@ import { BASE_UPLOADURL } from "@/config";
 import tree from "./components/tree/index.vue";
 import upload from "./components/upload/index.vue";
 import { type FormInstance } from "wot-design-uni/components/wd-form/types";
+import { createGlobalLoadingMiddleware } from "@/api/core/middleware";
 const titleArr = ["新增员工", "编辑员工"];
 const treePicker = ref();
 const form = ref<FormInstance>();
-const formData = ref({
-  roleId: "",
+const isShowInstructor = ref(false);
+const formData = reactive({
   status: 1,
   password: "",
   trainingPrograms: [],
-  certificateInnocence:
-    "https://b0.bdstatic.com/920d3e4f7d5d8aeb8680137b987fa262.jpg",
+  certificateInnocence: [],
   teachingPhilosophy: "",
   excelMsg: "",
   username: "",
+  selecteddeparts: [],
+  deptName: "",
+  selectedroles: "",
+  backgroundImg: [],
+  healthy: [],
+  honor: [],
+  realname: "",
 });
-const deptId = ref([]);
 const type = ref(0);
 const treeFlage = ref(false);
+const id = ref("");
 const { data: deptList } = useRequest(() => Apis.sys.findByDeptTree(), {});
 const { data: rolelist } = useRequest(() => Apis.sys.rolelist(), {});
 const { data: categoryList } = useRequest(() => Apis.app.appCategory(), {});
+const {
+  data,
+  send: handleAdd,
+  loading: isloading,
+} = useRequest((param) => Apis.sys.userAdd({ data: { ...param } }), {
+  immediate: false,
+  middleware: createGlobalLoadingMiddleware(),
+});
 
+const { send: queryById } = useRequest(
+  (id) => Apis.sys.queryById({ data: { id } }),
+  {
+    immediate: false,
+    middleware: createGlobalLoadingMiddleware(),
+  },
+);
 onLoad((query: any) => {
   type.value = query.type;
+  id.value = query.id;
+  if (type.value == 1) {
+    const res = queryById(query.id);
+    console.log(res, "场地");
+  }
 });
 function handleShow() {
   // treePicker.value._show();
   treeFlage.value = true;
-  console.log(treePicker.value);
 }
 function handleClickLeft() {
   uni.navigateBack();
 }
-function handleSubmit() {
-  console.log(deptId.value, "asd");
-
-  form.value?.validate();
-}
-function handleChangeDept(e) {
-  console.log(e);
+async function handleSubmit() {
+  const val = await form.value?.validate();
+  if (val?.valid) {
+    const obj = {
+      ...formData,
+      honor: transformImg("honor") || null,
+      backgroundImg: transformImg("backgroundImg") || null,
+      healthy: transformImg("healthy") || null,
+      trainingPrograms: transformImg("trainingPrograms") || null,
+      certificateInnocence: transformImg("certificateInnocence") || null,
+      selecteddeparts: formData.selecteddeparts.join(","),
+    };
+    console.log(obj, "提交表单");
+    type.value == 0 ? await handleAdd(obj) : await handleEdit(obj);
+    uni.navigateBack();
+  }
+  // console.log(val, "校验", formData);
 }
+function handleEdit(a: any) {}
 function handleSubmitDept() {
-  console.log(treePicker.value);
+  const key = treePicker.value.getCheckedKeys();
+  if (!key) return uni.showToast({ title: "最少选择一个部门", icon: "none" });
+  formData.deptName = findNamesByKeys(key, deptList.value).join(",");
+  treeFlage.value = false;
+  formData.selecteddeparts = key;
+}
+function handleRect() {
+  const key = treePicker.value.getCheckedKeys();
+  formData.selecteddeparts = [];
+  treePicker.value.setCheckedKeys(key, false);
+  formData.deptName = "";
+}
+function handleChangeRole(e: { value: string }) {
+  rolelist.value.records.forEach((item: any) => {
+    if (item.roleCode == "instructor") {
+      if (item.id == e.value) {
+        isShowInstructor.value = true;
+      } else {
+        isShowInstructor.value = false;
+      }
+    }
+  });
+}
+function transformImg(key: keyof typeof formData) {
+  const list = (formData[key] as any[]).map((it) => it.url).join(",");
+  return list;
+}
+
+/**
+ * 通过key数组在树形结构中查找对应的name数组
+ * @param keys 要查找的节点id数组
+ * @param treeData 树形数据
+ * @param valueField 节点值字段名,默认为'id'
+ * @param labelField 节点名称字段名,默认为'departName'
+ * @param childrenField 子节点字段名,默认为'children'
+ * @returns 找到的节点名称数组
+ */
+function findNamesByKeys(
+  keys: any[],
+  treeData: any[],
+  valueField: string = "id",
+  labelField: string = "departName",
+  childrenField: string = "children",
+): string[] {
+  if (!keys || keys.length === 0) return [];
+
+  const result: string[] = [];
+
+  // 递归查找单个key的name
+  function findNameByKey(key: any, nodes: any[]): string | null {
+    if (!nodes || nodes.length === 0) return null;
+
+    for (const node of nodes) {
+      // 如果当前节点匹配,返回该节点的名称
+      if (node[valueField] === key) {
+        return node[labelField] || "";
+      }
+
+      // 如果有子节点,递归查找
+      if (node[childrenField] && node[childrenField].length > 0) {
+        const foundName = findNameByKey(key, node[childrenField]);
+        if (foundName) {
+          return foundName;
+        }
+      }
+    }
+
+    return null;
+  }
+
+  // 遍历所有key查找对应的name
+  for (const key of keys) {
+    const name = findNameByKey(key, treeData);
+    if (name) {
+      result.push(name);
+    }
+  }
+
+  return result;
 }
-function handleRect() {}
 </script>
 
 <style scoped lang="scss">
@@ -245,6 +470,23 @@ function handleRect() {}
 :deep(.wd-card.is-rectangle .wd-card__content::after) {
   height: 0 !important;
 }
+.from {
+  :deep(.wd-cell) {
+    padding-left: 0 !important;
+    .wd-cell__wrapper {
+      padding: 0 !important;
+    }
+    .wd-cell__left {
+      display: none;
+    }
+  }
+  .customFormItem {
+    :deep(.input) {
+      margin-top: 0 !important;
+      padding-top: 0 !important;
+    }
+  }
+}
 </style>
 <route lang="json">
 {

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä