feat(用户管理): 添加微信会员绑定功能

- 在用户管理页面新增添加会员弹窗及表单
- 添加绑定微信会员的API接口
- 设置默认企业ID用于测试
- 实现表单验证及提交逻辑
This commit is contained in:
dzq 2025-11-26 15:20:31 +08:00
parent 9163a1e322
commit 59b19cd9e3
3 changed files with 118 additions and 2 deletions

View File

@ -107,6 +107,18 @@ export interface BindQyUserCommand {
idNum: string; idNum: string;
} }
export interface BindWxMpUserCommand {
/** 企业ID */
corpid: string;
/** 动态码 */
dynamicCode: string;
/** 真实姓名 */
name: string;
/** 身份证号码 */
idNum: string;
}
export const getAb98UserListApi = (params: Ab98UserQuery) => { export const getAb98UserListApi = (params: Ab98UserQuery) => {
return http.request<ResponseData<PageDTO<Ab98UserDTO>>>("get", "/ab98/users", { params }); return http.request<ResponseData<PageDTO<Ab98UserDTO>>>("get", "/ab98/users", { params });
}; };
@ -129,4 +141,9 @@ export const getAb98UserDetailApi = (id: number, corpid: string) => {
export const bindQyUserApi = (data: BindQyUserCommand) => { export const bindQyUserApi = (data: BindQyUserCommand) => {
return http.request<ResponseData<void>>("post", "/ab98/users/bindQyUser", { data }); return http.request<ResponseData<void>>("post", "/ab98/users/bindQyUser", { data });
};
export const bindWxMpUser = (data: BindWxMpUserCommand) => {
return http.request<ResponseData<void>>("post", "/ab98/users/bindWxMpUser", { data });
}; };

View File

@ -7,7 +7,7 @@ import { QyUserLoginDTO } from "@/api/common/login";
export const useWxStore = defineStore("wx", () => { export const useWxStore = defineStore("wx", () => {
// 授权企业id // 授权企业id
const corpid = ref<string>("") const corpid = ref<string>("wpZ1ZrEgAA2QTxIRcB4cMtY7hQbTcPAw")
// 微信授权 code // 微信授权 code
const code = ref<string>("") const code = ref<string>("")
// 防止 CSRF 攻击的 state 参数 // 防止 CSRF 攻击的 state 参数

View File

@ -1,14 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref, watch } from "vue"; import { onMounted, reactive, ref, watch } from "vue";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { getAb98UserListApi, type Ab98UserDTO, Ab98UserQuery } from "@/api/ab98/user"; import { getAb98UserListApi, type Ab98UserDTO, Ab98UserQuery, bindWxMpUser, type BindWxMpUserCommand } from "@/api/ab98/user";
import { type PaginationProps } from "@pureadmin/table"; import { type PaginationProps } from "@pureadmin/table";
import { CommonUtils } from "@/utils/common"; import { CommonUtils } from "@/utils/common";
import { useWxStore } from "@/store/modules/wx"; import { useWxStore } from "@/store/modules/wx";
import { ElMessage, ElMessageBox } from "element-plus";
import Search from "@iconify-icons/ep/search"; import Search from "@iconify-icons/ep/search";
import Refresh from "@iconify-icons/ep/refresh"; import Refresh from "@iconify-icons/ep/refresh";
import View from "@iconify-icons/ep/view"; import View from "@iconify-icons/ep/view";
import Plus from "@iconify-icons/ep/plus";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { getAb98UserTagNamesApi } from "@/api/ab98/tag"; import { getAb98UserTagNamesApi } from "@/api/ab98/tag";
@ -31,6 +33,17 @@ const searchFormParams = reactive<Ab98UserQuery & { search?: string }>({
search: undefined search: undefined
}); });
//
const dialogVisible = ref(false);
const addMemberFormRef = ref();
const addMemberLoading = ref(false);
const addMemberForm = reactive<BindWxMpUserCommand>({
corpid: wxStore.corpid || "",
dynamicCode: "",
name: "",
idNum: ""
});
const pageLoading = ref(false); const pageLoading = ref(false);
const dataList = ref<Ab98UserDTO[]>([]); const dataList = ref<Ab98UserDTO[]>([]);
const pagination = reactive<PaginationProps>({ const pagination = reactive<PaginationProps>({
@ -106,6 +119,58 @@ const handleViewDetail = (row: Ab98UserDTO) => {
}); });
}; };
//
const openAddMemberDialog = () => {
dialogVisible.value = true;
//
addMemberForm.dynamicCode = "";
addMemberForm.name = "";
addMemberForm.idNum = "";
};
//
const closeDialog = () => {
dialogVisible.value = false;
addMemberFormRef.value?.resetFields();
};
//
const submitAddMember = async () => {
if (!addMemberFormRef.value) return;
await addMemberFormRef.value.validate(async (valid) => {
if (valid) {
try {
addMemberLoading.value = true;
await bindWxMpUser(addMemberForm);
ElMessage.success("会员添加成功");
closeDialog();
//
getList();
} catch (error) {
console.error("添加会员失败:", error);
ElMessage.error("添加会员失败,请重试");
} finally {
addMemberLoading.value = false;
}
}
});
};
//
const addMemberRules = reactive({
dynamicCode: [
{ required: true, message: "请输入动态码", trigger: "blur" }
],
name: [
{ required: true, message: "请输入姓名", trigger: "blur" }
],
idNum: [
{ required: true, message: "请输入身份证号", trigger: "blur" },
{ pattern: /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/, message: "请输入正确的身份证号", trigger: "blur" }
] as any
});
onMounted(() => { onMounted(() => {
getList(); getList();
getAb98UserTagNamesApi().then(res => { getAb98UserTagNamesApi().then(res => {
@ -138,6 +203,13 @@ onMounted(() => {
重置 重置
</el-button> </el-button>
</el-form-item> </el-form-item>
<el-form-item class="space-item">
</el-form-item>
<el-form-item>
<el-button type="success" :icon="useRenderIcon(Plus)" @click="openAddMemberDialog">
添加会员
</el-button>
</el-form-item>
</el-form> </el-form>
<div class="grid-container"> <div class="grid-container">
@ -175,6 +247,29 @@ onMounted(() => {
</div> </div>
</div> </div>
</div> </div>
<!-- 添加会员弹窗 -->
<el-dialog v-model="dialogVisible" title="添加会员" width="500px" :close-on-click-modal="false">
<el-form ref="addMemberFormRef" :model="addMemberForm" :rules="addMemberRules" label-width="100px">
<el-form-item label="动态码" prop="dynamicCode">
<el-input v-model="addMemberForm.dynamicCode" placeholder="请输入动态码" />
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="addMemberForm.name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="身份证号" prop="idNum">
<el-input v-model="addMemberForm.idNum" placeholder="请输入身份证号" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="closeDialog">取消</el-button>
<el-button type="primary" :loading="addMemberLoading" @click="submitAddMember">
确认添加
</el-button>
</span>
</template>
</el-dialog>
</div> </div>
</template> </template>
@ -272,4 +367,8 @@ onMounted(() => {
padding: 0; padding: 0;
} }
} }
.space-item {
flex: 1;
width: 100%;
}
</style> </style>