浏览代码

✨ feat: 新增重设密码功能

WanGxC 1 年之前
父节点
当前提交
085fb4a7a6

+ 49 - 34
src/views/productCenter/productList/index.vue

@@ -1,66 +1,68 @@
 <script setup lang="ts">
-import { storeToRefs } from 'pinia'
-import { onBeforeUnmount, provide, ref } from 'vue'
-import { getCardData, getLineData, getLineMonthData, getLineWeekData } from './api'
-import DataTable from './components/DataTable.vue'
-import DataTendencyChart from './components/DateTendency/index.vue'
-import ProductLineDialog from './components/ProductDialog.vue'
-import ProductSelectCard from './components/ProductSelectCard.vue'
-import TopFilters from './components/TopFilter.vue'
-import useButtonGroup from './hooks/useButton'
-import { productListMetricsEnum } from './utils/enum'
-import { usePublicData } from '/@/stores/publicData'
-import { useShopInfo } from '/@/stores/shopInfo'
-import emitter from '/@/utils/emitter'
+import { storeToRefs } from 'pinia';
+import { onBeforeUnmount, provide, ref } from 'vue';
+import { getCardData, getLineData, getLineMonthData, getLineWeekData } from './api';
+import DataTable from './components/DataTable.vue';
+import DataTendencyChart from './components/DateTendency/index.vue';
+import ProductLineDialog from './components/ProductDialog.vue';
+import ProductSelectCard from './components/ProductSelectCard.vue';
+import TopFilters from './components/TopFilter.vue';
+import useButtonGroup from './hooks/useButton';
+import { productListMetricsEnum } from './utils/enum';
+import { usePublicData } from '/@/stores/publicData';
+import { useShopInfo } from '/@/stores/shopInfo';
+import emitter from '/@/utils/emitter';
 
 // 接收下拉框选中的值MetricsCards组件的queryParams
-const productlineId = ref('ALL')
+const productlineId = ref('ALL');
 emitter.on('TopFilters-selectValue', (value: any) => {
   // console.log('value', value.selectValue)
-  productlineId.value = value.selectValue
-})
+  productlineId.value = value.selectValue;
+});
 
 // 接收搜索框的值
-const searchItem = ref('')
+const searchItem = ref('');
 emitter.on('TopFilters-searchItem', (value: any) => {
-  searchItem.value = value.searchItem
-})
+  searchItem.value = value.searchItem;
+});
 
 // 店铺信息
-const shopInfo = useShopInfo()
-const { profile } = storeToRefs(shopInfo)
+const shopInfo = useShopInfo();
+const { profile } = storeToRefs(shopInfo);
 // 公共数据
-const publicData = usePublicData()
-const { dateRange } = storeToRefs(publicData)
+const publicData = usePublicData();
+const { dateRange } = storeToRefs(publicData);
 const queryParams = ref({
   profileId: profile.value.profile_id,
   dateRange,
   productlineId,
   searchItem,
-})
+});
 
-provide('dateRange', dateRange)
-provide('profile', profile)
+provide('dateRange', dateRange);
+provide('profile', profile);
 
 // 按钮组
-const { buttons, activeButton, activeStyle, handleClickBtn } = useButtonGroup()
-provide('activeButton', activeButton)
+const { buttons, activeButton, activeStyle, handleClickBtn } = useButtonGroup();
+provide('activeButton', activeButton);
 
 // 控制创建产品线弹窗
-function handleProductlog() {
-  emitter.emit('open-productLine-dialog', { isVisible: true })
+function handleCreateDialog() {
+  emitter.emit('open-productLine-dialog', { isVisible: true });
 }
 
 onBeforeUnmount(() => {
-  emitter.all.clear()
-})
+  emitter.all.clear();
+});
 </script>
 
 <template>
   <div class="outer-container">
     <TopFilters></TopFilters>
     <div class="table-tips">
-      <el-icon><Warning /></el-icon>
+      <el-icon>
+        <Warning />
+      </el-icon>
       <p class="prompt-words">商品中心广告数据统计不包含SB广告</p>
     </div>
     <el-card>
@@ -77,7 +79,13 @@ onBeforeUnmount(() => {
   <ProductSelectCard></ProductSelectCard>
   <div class="pl-and-asin-tables">
     <div class="asin-table-container">
-      <vxe-button class="custom-button" type="text" status="primary" content="创建产品线" icon="vxe-icon-add" @click="handleProductlog"></vxe-button>
+      <vxe-button
+        class="custom-button"
+        type="text"
+        status="primary"
+        content="创建产品线"
+        icon="vxe-icon-add"
+        @click="handleCreateDialog"></vxe-button>
       <div class="xp-radio-group-wrapper">
         <el-button-group>
           <el-button
@@ -101,15 +109,18 @@ onBeforeUnmount(() => {
 .outer-container {
   padding: 5px 10px 0 10px;
 }
+
 .table-tips {
   color: #8d9095;
   display: flex;
   align-items: center;
   margin: 8px 0;
 }
+
 .pl-and-asin-tables {
   padding: 10px 12px 0 12px;
 }
+
 .asin-table-container {
   width: 100%;
   padding: 8px;
@@ -118,19 +129,23 @@ onBeforeUnmount(() => {
   border-radius: 4px;
   box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.06);
 }
+
 .xp-radio-group-wrapper {
   display: flex;
   justify-content: flex-end;
   margin-bottom: 12px;
   align-items: center;
 }
+
 .custom-button {
   position: absolute;
   color: #3a83f7 !important;
 }
+
 ::v-deep(.el-form--default.el-form--label-top .el-form-item .el-form-item__label) {
   color: red;
 }
+
 .rompt-words {
   margin-left: 3px;
 }

+ 47 - 38
src/views/system/user/api.ts

@@ -1,58 +1,67 @@
-import { request,downloadFile } from '/@/utils/service';
+import { request, downloadFile } from '/@/utils/service';
 import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
 
 export const apiPrefix = '/api/system/user/';
 
 export function GetDept(query: PageQuery) {
-    return request({
-        url: "/api/system/dept/dept_lazy_tree/",
-        method: 'get',
-        params: query,
-    });
+  return request({
+    url: '/api/system/dept/dept_lazy_tree/',
+    method: 'get',
+    params: query,
+  });
 }
 
 export function GetList(query: PageQuery) {
-    return request({
-        url: apiPrefix,
-        method: 'get',
-        params: query,
-    });
+  return request({
+    url: apiPrefix,
+    method: 'get',
+    params: query,
+  });
 }
+
 export function GetObj(id: InfoReq) {
-    return request({
-        url: apiPrefix + id,
-        method: 'get',
-    });
+  return request({
+    url: apiPrefix + id,
+    method: 'get',
+  });
 }
 
 export function AddObj(obj: AddReq) {
-    return request({
-        url: apiPrefix,
-        method: 'post',
-        data: obj,
-    });
+  return request({
+    url: apiPrefix,
+    method: 'post',
+    data: obj,
+  });
 }
 
 export function UpdateObj(obj: EditReq) {
-    return request({
-        url: apiPrefix + obj.id + '/',
-        method: 'put',
-        data: obj,
-    });
+  return request({
+    url: apiPrefix + obj.id + '/',
+    method: 'put',
+    data: obj,
+  });
 }
 
 export function DelObj(id: DelReq) {
-    return request({
-        url: apiPrefix + id + '/',
-        method: 'delete',
-        data: { id },
-    });
-}
-
-export function exportData(params:any){
-    return downloadFile({
-        url: apiPrefix + 'export_data/',
-        params: params,
-        method: 'get'
-    })
+  return request({
+    url: apiPrefix + id + '/',
+    method: 'delete',
+    data: { id },
+  });
+}
+
+export function exportData(params: any) {
+  return downloadFile({
+    url: apiPrefix + 'export_data/',
+    params: params,
+    method: 'get',
+  });
+}
+
+export function postResetPassword(body: any, id: number) {
+  return request({
+    url: apiPrefix + `${id}/reset_password/`,
+    method: 'put',
+    data: body,
+  });
 }

+ 375 - 359
src/views/system/user/crud.tsx

@@ -1,373 +1,389 @@
 import * as api from './api';
-import { dict, UserPageQuery, AddReq, DelReq, EditReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
+import {
+  dict,
+  UserPageQuery,
+  AddReq,
+  DelReq,
+  EditReq,
+  compute,
+  CreateCrudOptionsProps,
+  CreateCrudOptionsRet,
+} from '@fast-crud/fast-crud';
 import { request } from '/@/utils/service';
 import { dictionary } from '/@/utils/dictionary';
 import { successMessage } from '/@/utils/message';
-import { inject } from 'vue';
+import { inject, ref } from 'vue';
+import resetPassword from './resetPassword';
 
 export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
-	const pageRequest = async (query: UserPageQuery) => {
-		return await api.GetList(query);
-	};
-	const editRequest = async ({ form, row }: EditReq) => {
-		form.id = row.id;
-		return await api.UpdateObj(form);
-	};
-	const delRequest = async ({ row }: DelReq) => {
-		return await api.DelObj(row.id);
-	};
-	const addRequest = async ({ form }: AddReq) => {
-		return await api.AddObj(form);
-	};
+  const pageRequest = async (query: UserPageQuery) => {
+    return await api.GetList(query);
+  };
+  const editRequest = async ({ form, row }: EditReq) => {
+    form.id = row.id;
+    return await api.UpdateObj(form);
+  };
+  const delRequest = async ({ row }: DelReq) => {
+    return await api.DelObj(row.id);
+  };
+  const addRequest = async ({ form }: AddReq) => {
+    return await api.AddObj(form);
+  };
 
-	const exportRequest = async (query: UserPageQuery) => {
-		return await api.exportData(query)
-	}
+  const exportRequest = async (query: UserPageQuery) => {
+    return await api.exportData(query);
+  };
 
-	//权限判定
-	const hasPermissions:any = inject('$hasPermissions');
+  //权限判定
+  const hasPermissions: any = inject('$hasPermissions');
 
-	return {
-		crudOptions: {
-			table: {
-				remove: {
-					confirmMessage: '是否删除该用户?',
-				},
-			},
-			request: {
-				pageRequest,
-				addRequest,
-				editRequest,
-				delRequest,
-			},
-			actionbar: {
-				buttons: {
-					add: {
-						show: hasPermissions('user:Create')
-						// show:true
-					},
-					export:{
-						text:"导出",//按钮文字
-						title:"导出",//鼠标停留显示的信息
-						click(){
-							return exportRequest(crudExpose!.getSearchFormData())
-						}
-					}
-				}
-			},
-			rowHandle: {
-				//固定右侧
-				fixed: 'right',
-				width: 200,
-				buttons: {
-					view: {
-						show: false,
-					},
-					edit: {
-						iconRight: 'Edit',
-						type: 'text',
-						show: hasPermissions('user:Update'),
-					},
-					remove: {
-						iconRight: 'Delete',
-						type: 'text',
-						show: hasPermissions('user:Delete'),
-					},
-                    custom: {
-						text: '重设密码',
-						type: 'text',
-						show: hasPermissions('user:ResetPassword'),
-						tooltip: {
-							placement: 'top',
-							content: '重设密码',
-						},
-						//@ts-ignore
-						click: (ctx: any) => {
-							const { row } = ctx;
-						},
-					},
-				},
-			},
-			columns: {
-				_index: {
-					title: '序号',
-					form: { show: false },
-					column: {
-						type: 'index',
-						align: 'center',
-						width: '70px',
-						columnSetDisabled: true, //禁止在列设置中选择
-					},
-				},
-				username: {
-					title: '账号',
-					search: {
-						show: true,
-					},
-					type: 'input',
-					column: {
-						minWidth: 100, //最小列宽
-					},
-					form: {
-						rules: [
-							// 表单校验规则
-							{
-								required: true,
-								message: '账号必填项',
-							},
-						],
-						component: {
-							placeholder: '请输入账号',
-						},
-					},
-				},
-				password: {
-					title: '密码',
-					type: 'input',
-					column: {
-						show: false,
-					},
-					editForm: {
-						show: false,
-					},
-					form: {
-						rules: [
-							// 表单校验规则
-							{
-								required: true,
-								message: '密码必填项',
-							},
-						],
-						component: {
-							span: 12,
-							showPassword: true,
-							placeholder: '请输入密码',
-						},
-						// value: vm.systemConfig('base.default_password'),
-					},
-					/* valueResolve(row, key) {
+  const { dialogVisible, isShowDialog, resetId } = resetPassword();
+
+  return {
+    dialogVisible,
+    resetId,
+    crudOptions: {
+      table: {
+        remove: {
+          confirmMessage: '是否删除该用户?',
+        },
+      },
+      request: {
+        pageRequest,
+        addRequest,
+        editRequest,
+        delRequest,
+      },
+      actionbar: {
+        buttons: {
+          add: {
+            show: hasPermissions('user:Create'),
+            // show:true
+          },
+          export: {
+            text: '导出', //按钮文字
+            title: '导出', //鼠标停留显示的信息
+            click() {
+              return exportRequest(crudExpose!.getSearchFormData());
+            },
+          },
+        },
+      },
+      rowHandle: {
+        //固定右侧
+        fixed: 'right',
+        width: 200,
+        buttons: {
+          view: {
+            show: false,
+          },
+          edit: {
+            iconRight: 'Edit',
+            type: 'text',
+            show: hasPermissions('user:Update'),
+          },
+          remove: {
+            iconRight: 'Delete',
+            type: 'text',
+            show: hasPermissions('user:Delete'),
+          },
+          custom: {
+            text: '重设密码',
+            type: 'text',
+            show: hasPermissions('user:ResetPassword'),
+            tooltip: {
+              placement: 'top',
+              content: '重设密码',
+            },
+            //@ts-ignore
+            click: (ctx: any) => {
+              const { row } = ctx;
+              console.log('row', row);
+              isShowDialog(row.id);
+            },
+          },
+        },
+      },
+      columns: {
+        _index: {
+          title: '序号',
+          form: { show: false },
+          column: {
+            type: 'index',
+            align: 'center',
+            width: '70px',
+            columnSetDisabled: true, //禁止在列设置中选择
+          },
+        },
+        username: {
+          title: '账号',
+          search: {
+            show: true,
+          },
+          type: 'input',
+          column: {
+            minWidth: 100, //最小列宽
+          },
+          form: {
+            rules: [
+              // 表单校验规则
+              {
+                required: true,
+                message: '账号必填项',
+              },
+            ],
+            component: {
+              placeholder: '请输入账号',
+            },
+          },
+        },
+        password: {
+          title: '密码',
+          type: 'input',
+          column: {
+            show: false,
+          },
+          editForm: {
+            show: false,
+          },
+          form: {
+            rules: [
+              // 表单校验规则
+              {
+                required: true,
+                message: '密码必填项',
+              },
+            ],
+            component: {
+              span: 12,
+              showPassword: true,
+              placeholder: '请输入密码',
+            },
+            // value: vm.systemConfig('base.default_password'),
+          },
+          /* valueResolve(row, key) {
                         if (row.password) {
                             row.password = vm.$md5(row.password)
                         }
                     } */
-				},
-				name: {
-					title: '姓名',
-					search: {
-						show: true,
-					},
-					type: 'input',
-					column: {
-						minWidth: 100, //最小列宽
-					},
-					form: {
-						rules: [
-							// 表单校验规则
-							{
-								required: true,
-								message: '姓名必填项',
-							},
-						],
-						component: {
-							span: 12,
-							placeholder: '请输入姓名',
-						},
-					},
-				},
-				dept: {
-					title: '部门',
-					search: {
-						disabled: true,
-					},
-					type: 'dict-tree',
-					dict: dict({
-						isTree: true,
-						url: '/api/system/dept/all_dept/',
-						value: 'id',
-						label: 'name',
-						getData: async ({ url }: { url: string }) => {
-							return request({
-								url: url,
-							}).then((ret: any) => {
-								return ret.data;
-							});
-						},
-					}),
-					column: {
-						minWidth: 150, //最小列宽
-					},
-					form: {
-						rules: [
-							// 表单校验规则
-							{
-								required: true,
-								message: '必填项',
-							},
-						],
-						component: {
-							filterable: true,
-							placeholder: '请选择',
-							props: {
-								props: {
-									value: 'id',
-									label: 'name',
-								},
-							},
-						},
-					},
-				},
-				role: {
-					title: '角色',
-					search: {
-						disabled: true,
-					},
-					type: 'dict-select',
-					dict: dict({
-						url: '/api/system/role/',
-						value: 'id',
-						label: 'name',
-						isTree: true,
-						getData: async ({ url }: { url: string }) => {
-							return request({
-								url: url,
-								params: {
-									page: 1,
-									limit: 10,
-								},
-							}).then((ret: any) => {
-								return ret.data;
-							});
-						},
-					}),
-					column: {
-						minWidth: 100, //最小列宽
-					},
-					form: {
-						rules: [
-							// 表单校验规则
-							{
-								required: true,
-								message: '必填项',
-							},
-						],
-						component: {
-							multiple: true,
-							filterable: true,
-							placeholder: '请选择角色',
-						},
-					},
-				},
-				mobile: {
-					title: '手机号码',
-					search: {
-						show: true,
-					},
-					type: 'input',
-					column: {
-						minWidth: 120, //最小列宽
-					},
-					form: {
-						rules: [
-							{
-								max: 20,
-								message: '请输入正确的手机号码',
-								trigger: 'blur',
-							},
-							{
-								pattern: /^1[3-9]\d{9}$/,
-								message: '请输入正确的手机号码',
-							},
-						],
-						component: {
-							placeholder: '请输入手机号码',
-						},
-					},
-				},
-				email: {
-					title: '邮箱',
-					column: {
-						width: 260,
-					},
-					form: {
-						rules: [
-							{
-								type: 'email',
-								message: '请输入正确的邮箱地址',
-								trigger: ['blur', 'change'],
-							},
-						],
-						component: {
-							placeholder: '请输入邮箱',
-						},
-					},
-				},
-				gender: {
-					title: '性别',
-					type: 'dict-select',
-					dict: dict({
-						data: dictionary('gender'),
-					}),
-					form: {
-						value: 1,
-						component: {
-							span: 12,
-						},
-					},
-					component: { props: { color: 'auto' } }, // 自动染色
-				},
-				user_type: {
-					title: '用户类型',
-					search: {
-						show: true,
-					},
-					type: 'dict-select',
-					dict: dict({
-						data: dictionary('user_type'),
-					}),
-					column: {
-						minWidth: 100, //最小列宽
-					},
-					form: {
-						show: false,
-						value: 0,
-						component: {
-							span: 12,
-						},
-					},
-				},
-				is_active: {
-					title: '锁定',
-					search: {
-						show: true,
-					},
-					type: 'dict-radio',
-					column: {
-						component: {
-							name: 'fs-dict-switch',
-							activeText: '',
-							inactiveText: '',
-							style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6',
-							onChange: compute((context) => {
-								return () => {
-									api.UpdateObj(context.row).then((res: APIResponseData) => {
-										successMessage(res.msg as string);
-									});
-								};
-							}),
-						},
-					},
-					dict: dict({
-						data: dictionary('button_status_bool'),
-					}),
-				},
-				avatar: {
-					title: '头像',
-					type: 'avatar-cropper',
-					form: {
-						show: false,
-					},
-				},
-			},
-		},
-	};
+        },
+        name: {
+          title: '姓名',
+          search: {
+            show: true,
+          },
+          type: 'input',
+          column: {
+            minWidth: 100, //最小列宽
+          },
+          form: {
+            rules: [
+              // 表单校验规则
+              {
+                required: true,
+                message: '姓名必填项',
+              },
+            ],
+            component: {
+              span: 12,
+              placeholder: '请输入姓名',
+            },
+          },
+        },
+        dept: {
+          title: '部门',
+          search: {
+            disabled: true,
+          },
+          type: 'dict-tree',
+          dict: dict({
+            isTree: true,
+            url: '/api/system/dept/all_dept/',
+            value: 'id',
+            label: 'name',
+            getData: async ({ url }: { url: string }) => {
+              return request({
+                url: url,
+              }).then((ret: any) => {
+                return ret.data;
+              });
+            },
+          }),
+          column: {
+            minWidth: 150, //最小列宽
+          },
+          form: {
+            rules: [
+              // 表单校验规则
+              {
+                required: true,
+                message: '必填项',
+              },
+            ],
+            component: {
+              filterable: true,
+              placeholder: '请选择',
+              props: {
+                props: {
+                  value: 'id',
+                  label: 'name',
+                },
+              },
+            },
+          },
+        },
+        role: {
+          title: '角色',
+          search: {
+            disabled: true,
+          },
+          type: 'dict-select',
+          dict: dict({
+            url: '/api/system/role/',
+            value: 'id',
+            label: 'name',
+            isTree: true,
+            getData: async ({ url }: { url: string }) => {
+              return request({
+                url: url,
+                params: {
+                  page: 1,
+                  limit: 10,
+                },
+              }).then((ret: any) => {
+                return ret.data;
+              });
+            },
+          }),
+          column: {
+            minWidth: 100, //最小列宽
+          },
+          form: {
+            rules: [
+              // 表单校验规则
+              {
+                required: true,
+                message: '必填项',
+              },
+            ],
+            component: {
+              multiple: true,
+              filterable: true,
+              placeholder: '请选择角色',
+            },
+          },
+        },
+        mobile: {
+          title: '手机号码',
+          search: {
+            show: true,
+          },
+          type: 'input',
+          column: {
+            minWidth: 120, //最小列宽
+          },
+          form: {
+            rules: [
+              {
+                max: 20,
+                message: '请输入正确的手机号码',
+                trigger: 'blur',
+              },
+              {
+                pattern: /^1[3-9]\d{9}$/,
+                message: '请输入正确的手机号码',
+              },
+            ],
+            component: {
+              placeholder: '请输入手机号码',
+            },
+          },
+        },
+        email: {
+          title: '邮箱',
+          column: {
+            width: 260,
+          },
+          form: {
+            rules: [
+              {
+                type: 'email',
+                message: '请输入正确的邮箱地址',
+                trigger: ['blur', 'change'],
+              },
+            ],
+            component: {
+              placeholder: '请输入邮箱',
+            },
+          },
+        },
+        gender: {
+          title: '性别',
+          type: 'dict-select',
+          dict: dict({
+            data: dictionary('gender'),
+          }),
+          form: {
+            value: 1,
+            component: {
+              span: 12,
+            },
+          },
+          component: { props: { color: 'auto' } }, // 自动染色
+        },
+        user_type: {
+          title: '用户类型',
+          search: {
+            show: true,
+          },
+          type: 'dict-select',
+          dict: dict({
+            data: dictionary('user_type'),
+          }),
+          column: {
+            minWidth: 100, //最小列宽
+          },
+          form: {
+            show: false,
+            value: 0,
+            component: {
+              span: 12,
+            },
+          },
+        },
+        is_active: {
+          title: '锁定',
+          search: {
+            show: true,
+          },
+          type: 'dict-radio',
+          column: {
+            component: {
+              name: 'fs-dict-switch',
+              activeText: '',
+              inactiveText: '',
+              style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6',
+              onChange: compute((context) => {
+                return () => {
+                  api.UpdateObj(context.row).then((res: APIResponseData) => {
+                    successMessage(res.msg as string);
+                  });
+                };
+              }),
+            },
+          },
+          dict: dict({
+            data: dictionary('button_status_bool'),
+          }),
+        },
+        avatar: {
+          title: '头像',
+          type: 'avatar-cropper',
+          form: {
+            show: false,
+          },
+        },
+      },
+    },
+  };
 };

+ 204 - 97
src/views/system/user/index.vue

@@ -1,142 +1,249 @@
-<template>
-	<fs-page>
-		<el-row class="mx-2">
-			<el-col xs="24" :sm="8" :md="6" :lg="4" :xl="4" class="p-1">
-				<el-card :body-style="{ height: '100%' }">
-					<p class="font-mono font-black text-center text-xl pb-5">
-						部门列表
-						<el-tooltip effect="dark" :content="content" placement="right">
-							<el-icon>
-								<QuestionFilled />
-							</el-icon>
-						</el-tooltip>
-					</p>
-					<el-input v-model="filterText" :placeholder="placeholder" />
-					<el-tree ref="treeRef" class="font-mono font-bold leading-6 text-7xl" :data="data" :props="treeProps"
-						:filter-node-method="filterNode" icon="ArrowRightBold" :indent="12" @node-click="onTreeNodeClick">
-						<template #default="{ node, data }">
-							<span class="text-center font-black font-normal">{{ node.label }}</span>
-						</template>
-					</el-tree>
-				</el-card>
-			</el-col>
-			<el-col xs="24" :sm="16" :md="18" :lg="20" :xl="20" class="p-1">
-				<el-card :body-style="{ height: '100%' }">
-					<fs-crud ref="crudRef" v-bind="crudBinding">
-						<template #actionbar-right>
-							<importExcel api="api/system/user/">导入 </importExcel>
-						</template>
-					</fs-crud>
-				</el-card>
-			</el-col>
-		</el-row>
-
-	</fs-page>
-</template>
-
 <script lang="ts" setup name="user">
-import { useExpose, useCrud } from '@fast-crud/fast-crud';
+import { useCrud, useExpose } from '@fast-crud/fast-crud';
 import { createCrudOptions } from './crud';
 import * as api from './api';
+import { ElMessage, FormInstance, FormRules } from 'element-plus';
 import { ElTree } from 'element-plus';
-import { ref, onMounted, watch, toRaw } from 'vue';
+import { onMounted, reactive, ref, toRaw, watch } from 'vue';
 import XEUtils from 'xe-utils';
-import importExcel from '/@/components/importExcel/index.vue'
+import importExcel from '/@/components/importExcel/index.vue';
+
+const crudRef = ref(); // crud组件的ref
+const crudBinding = ref(); // crud 配置的ref
+const { crudExpose } = useExpose({ crudRef, crudBinding }); // 暴露的方法
+const { crudOptions, dialogVisible, resetId } = createCrudOptions({ crudExpose }); // 你的crud配置
+const { resetCrudOptions } = useCrud({ crudExpose, crudOptions }); // 初始化crud配置
 
 interface Tree {
-	id: number;
-	name: string;
-	status: boolean;
-	children?: Tree[];
+  id: number;
+  name: string;
+  status: boolean;
+  children?: Tree[];
 }
 
 interface APIResponseData {
-	code?: number;
-	data: [];
-	msg?: string;
+  code?: number;
+  data: [];
+  msg?: string;
 }
 
 // 引入组件
 const placeholder = ref('请输入部门名称');
 const filterText = ref('');
 const treeRef = ref<InstanceType<typeof ElTree>>();
-
 const treeProps = {
-	children: 'children',
-	label: 'name',
-	icon: 'icon',
+  children: 'children',
+  label: 'name',
+  icon: 'icon',
 };
-
-watch(filterText, (val) => {
-	treeRef.value!.filter(val);
-});
-
-const filterNode = (value: string, data: Tree) => {
-	if (!value) return true;
-	return toRaw(data).name.indexOf(value) !== -1;
-};
-
 let data = ref([]);
-
 const content = `
 1.部门信息;
 `;
 
-const getData = () => {
-	api.GetDept({}).then((ret: APIResponseData) => {
-		const responseData = ret.data;
-		const result = XEUtils.toArrayTree(responseData, {
-			parentKey: 'parent',
-			children: 'children',
-			strict: true,
-		});
-
-		data.value = result;
-	});
-};
+function filterNode(value: string, data: Tree) {
+  if (!value) return true;
+  return toRaw(data).name.indexOf(value) !== -1;
+}
 
-//树形点击事件
-const onTreeNodeClick = (node: any) => {
-	const { id } = node;
-	crudExpose.doSearch({ form: { dept: id } });
-};
+function getData() {
+  api.GetDept({}).then((ret: APIResponseData) => {
+    const responseData = ret.data;
+    const result = XEUtils.toArrayTree(responseData, {
+      parentKey: 'parent',
+      children: 'children',
+      strict: true,
+    });
+
+    data.value = result;
+  });
+}
+
+/**
+ * 树形点击数据
+ * @param node
+ */
+function onTreeNodeClick(node: any) {
+  const { id } = node;
+  crudExpose.doSearch({ form: { dept: id } });
+}
+
+watch(filterText, (val) => {
+  treeRef.value!.filter(val);
+});
 
 // 页面打开后获取列表数据
 onMounted(() => {
-	getData();
+  getData();
 });
 
-// crud组件的ref
-const crudRef = ref();
-// crud 配置的ref
-const crudBinding = ref();
-// 暴露的方法
-const { crudExpose } = useExpose({ crudRef, crudBinding });
-// 你的crud配置
-const { crudOptions } = createCrudOptions({ crudExpose });
-// 初始化crud配置
-const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
-
 // 页面打开后获取列表数据
 onMounted(() => {
-	crudExpose.doRefresh();
+  crudExpose.doRefresh();
 });
+
+const confirmLoading = ref(false);
+const ruleFormRef = ref<FormInstance>();
+const ruleForm = reactive({
+  pass: '',
+  checkPass: '',
+});
+
+const rules = reactive<FormRules<typeof ruleForm>>({
+  pass: [{ validator: validatePass, trigger: 'blur' }],
+  checkPass: [{ validator: validatePass2, trigger: 'blur' }],
+});
+
+function validatePass(rule: any, value: any, callback: any) {
+  if (value === '') {
+    callback(new Error('Please input the password'));
+  } else {
+    if (ruleForm.checkPass !== '') {
+      if (!ruleFormRef.value) return;
+      ruleFormRef.value.validateField('checkPass');
+    }
+    callback();
+  }
+}
+
+function validatePass2(rule: any, value: any, callback: any) {
+  if (value === '') {
+    callback(new Error('Please input the password again'));
+  } else if (value !== ruleForm.pass) {
+    callback(new Error("Two inputs don't match!"));
+  } else {
+    callback();
+  }
+}
+
+async function submitForm(formEl: FormInstance | undefined) {
+  if (!formEl) return;
+  await formEl.validate((valid, fields) => {
+    if (valid) {
+      submitPwd();
+    } else {
+      console.log('error submit!', fields);
+    }
+  });
+}
+
+function resetForm(formEl: FormInstance | undefined) {
+  if (!formEl) return;
+  formEl.resetFields();
+  dialogVisible.value = false;
+}
+
+async function submitPwd() {
+  confirmLoading.value = true;
+  const id = resetId.value;
+  const body = {
+    newPassword: ruleForm.pass,
+    newPassword2: ruleForm.checkPass,
+  };
+  try {
+    const response = await api.postResetPassword(body, id);
+    if (response.code === 2000) {
+      dialogVisible.value = false;
+      empty();
+      ElMessage.success(response.msg);
+    } else {
+      ElMessage.error(response.msg);
+    }
+  } catch (error) {
+    console.log('error:', error);
+  } finally {
+    confirmLoading.value = false;
+  }
+}
+
+/**
+ * 关闭前清空表单, 统一控制器
+ * @param done
+ */
+function handleBeforeClose(done: Function) {
+  empty();
+  done();
+}
+
+/**
+ * 清空表单
+ */
+function empty() {
+  if (ruleFormRef.value) ruleFormRef.value.resetFields();
+}
 </script>
 
+<template>
+  <fs-page>
+    <el-row class="mx-2">
+      <el-col xs="24" :sm="8" :md="6" :lg="4" :xl="4" class="p-1">
+        <el-card :body-style="{ height: '100%' }">
+          <p class="font-mono font-black text-center text-xl pb-5">
+            部门列表
+            <el-tooltip effect="dark" :content="content" placement="right">
+              <el-icon>
+                <QuestionFilled />
+              </el-icon>
+            </el-tooltip>
+          </p>
+          <el-input v-model="filterText" :placeholder="placeholder" />
+          <el-tree
+            ref="treeRef"
+            class="font-mono font-bold leading-6 text-7xl"
+            :data="data"
+            :props="treeProps"
+            :filter-node-method="filterNode"
+            icon="ArrowRightBold"
+            :indent="12"
+            @node-click="onTreeNodeClick">
+            <template #default="{ node, data }">
+              <span class="text-center font-black font-normal">{{ node.label }}</span>
+            </template>
+          </el-tree>
+        </el-card>
+      </el-col>
+      <el-col xs="24" :sm="16" :md="18" :lg="20" :xl="20" class="p-1">
+        <el-card :body-style="{ height: '100%' }">
+          <fs-crud ref="crudRef" v-bind="crudBinding">
+            <template #actionbar-right>
+              <importExcel api="api/system/user/">导入</importExcel>
+            </template>
+          </fs-crud>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <el-dialog v-model="dialogVisible" title="修改密码" width="500" :before-close="handleBeforeClose">
+      <el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" :rules="rules" label-width="auto" status-icon>
+        <el-form-item label="Password" prop="pass">
+          <el-input v-model="ruleForm.pass" type="password" autocomplete="off" />
+        </el-form-item>
+        <el-form-item label="Confirm" prop="checkPass">
+          <el-input v-model="ruleForm.checkPass" type="password" autocomplete="off" />
+        </el-form-item>
+        <el-form-item class="flex-col">
+          <el-button @click="resetForm(ruleFormRef)">取消</el-button>
+          <el-button type="primary" @click="submitForm(ruleFormRef)" :loading="confirmLoading">确认</el-button>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
+  </fs-page>
+</template>
+
 <style lang="scss" scoped>
 .el-row {
-	height: 100%;
+  height: 100%;
 
-	.el-col {
-		height: 100%;
-	}
+  .el-col {
+    height: 100%;
+  }
 }
 
 .el-card {
-	height: 100%;
+  height: 100%;
 }
 
 .font-normal {
-	font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif;
+  font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif;
 }
 </style>

+ 17 - 0
src/views/system/user/resetPassword.ts

@@ -0,0 +1,17 @@
+import { ref } from 'vue';
+
+export default function resetPassword() {
+  const dialogVisible = ref(false);
+  const resetId = ref(0);
+
+  function isShowDialog(id: number) {
+    dialogVisible.value = true;
+    resetId.value = id;
+  }
+
+  return {
+    dialogVisible,
+    resetId,
+    isShowDialog,
+  };
+}