初始化询价智能体新仓:同步本地工程并排除密钥与 .env。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
linmianxun
2026-09-13 14:30:44 +08:00
co-authored by Cursor
commit 2a14def882
2621 changed files with 393165 additions and 0 deletions
@@ -0,0 +1,190 @@
<template>
<div class="account-padding">
<div class="my-account">账户</div>
<div class="account-row-item clearfix">
<div class="account-label gray-75">手机</div>
<span class="gray">{{ userDetail.phone ? userDetail.phone : '未填写' }}</span>
<span class="pointer blue-e5 phone-margin" @click="updatePhone" v-if="userDetail.phone">修改</span>
<span class="pointer blue-e5" @click="unbindPhone" v-if="userDetail.phone">解绑</span>
<span class="pointer blue-e5" @click="unbindPhone" v-else>绑定</span>
</div>
<div class="account-row-item clearfix">
<div class="account-label gray-75">邮箱</div>
<span class="gray">{{ userDetail.email ? userDetail.email : '未填写' }}</span>
<span class="pointer blue-e5 phone-margin" @click="updateEmail">修改</span>
<span class="pointer blue-e5" @click="unbindEmail" v-if="userDetail.email">解绑</span>
<span class="pointer blue-e5" @click="unbindEmail" v-else>绑定</span>
<span class="pointer blue-e5" style="margin-left:5px" @click="checkEmail" v-if="userDetail.email">验证</span>
</div>
<div class="account-row-item">
<div class="account-label gray-75">密码</div>
<Icon icon="ant-design:lock-outlined" style="color: #9e9e9e"/>
<span class="pointer blue-e5" style="margin-left: 10px" @click="updatePassWord">修改</span>
</div>
<div class="account-row-item clearfix">
<div class="account-label gray-75">账户注销</div>
<span style="color: red" class="pointer" @click="cancellation">注销</span>
</div>
</div>
<UserReplacePhoneModal @register="registerModal" @success="initUserDetail" />
<UserReplaceEmailModal @register="registerEmailModal" @success="initUserDetail" />
<UserPasswordModal @register="registerPassModal" @success="initUserDetail" />
</template>
<script lang="ts" setup>
import { onMounted, ref, reactive } from "vue";
import { CollapseContainer } from "/@/components/Container";
import { getUserData } from "./UserSetting.api";
import { useUserStore } from "/@/store/modules/user";
import UserReplacePhoneModal from "./commponents/UserPhoneModal.vue";
import UserReplaceEmailModal from "./commponents/UserEmailModal.vue";
import UserPasswordModal from "./commponents/UserPasswordModal.vue";
import { useModal } from "/@/components/Modal";
import { WechatFilled } from '@ant-design/icons-vue';
const userDetail = ref<any>([]);
const userStore = useUserStore();
const [registerModal, { openModal }] = useModal();
const [registerEmailModal, { openModal: openEmailModal }] = useModal();
const [registerPassModal, { openModal: openPassModal }] = useModal();
const wechatData = reactive<any>({
bindWechat: false,
name: '昵称',
});
/**
* 初始化用户数据
*/
function initUserDetail() {
//获取用户数据
getUserData().then((res => {
if (res.success) {
userDetail.value = res.result;
}
}));
}
/**
* 修改手机号
*/
function updatePhone() {
openModal(true, {
record: { phone: userDetail.value.phone, username: userDetail.value.username, id: userDetail.value.id }
});
}
/**
* 修改邮箱
*/
function updateEmail() {
openEmailModal(true, {
record: { email: userDetail.value.email, id: userDetail.value.id }
});
}
/**
* 密码修改
*/
function updatePassWord() {
openPassModal(true, {
record: { username: userDetail.value.username }
});
}
/**
* 手机号解绑
*/
function unbindPhone() {
console.log('手机号解绑');
}
/**
* 邮箱解绑
*/
function unbindEmail() {
console.log('邮箱解绑');
}
/**
* 邮箱验证
*/
function checkEmail() {
console.log('邮箱验证');
}
/**
* 微信绑定解绑事件
*/
function wechatBind() {
console.log('微信绑定解绑事件');
}
/**
* 注销事件
*/
function cancellation() {
}
onMounted(() => {
initUserDetail();
});
</script>
<style lang="less" scoped>
.account-row-item {
align-items: center;
/*begin 兼容暗夜模式*/
border-bottom: 1px solid @border-color-base;
/*end 兼容暗夜模式*/
box-sizing: border-box;
display: flex;
height: 71px;
position: relative;
}
.account-label {
text-align: left;
width: 160px;
}
.gray-75 {
/*begin 兼容暗夜模式*/
color: @text-color !important;
/*end 兼容暗夜模式*/
}
.pointer {
cursor: pointer;
}
.blue-e5 {
color: #1e88e5;
}
.phone-margin {
margin-left: 24px;
margin-right: 24px;
}
.clearfix:after {
clear: both;
}
.clearfix:before {
content: "";
display: table;
}
.account-padding{
padding: 30px 40px 0 20px;
}
.my-account{
font-size: 17px;
font-weight: 700!important;
/*begin 兼容暗夜模式*/
color: @text-color;
/*end 兼容暗夜模式*/
margin-bottom: 20px;
}
</style>
@@ -0,0 +1,360 @@
<template>
<div class="account-padding">
<div class="user-setting-top">
<div class="account-avatar">
<CropperAvatar
:uploadApi="uploadImg"
:showBtn="false"
:value="avatar"
:btnProps="{ preIcon: 'ant-design:cloud-upload-outlined' }"
@change="updateAvatar"
width="80"
/>
<div class="account-right">
<div v-if="!isEdit">
<span class="font-size-17 account-name">{{ userInfo.realname }}</span>
<a-tooltip content="编辑姓名">
<Icon class="pointer font-size-17 gray-bd account-icon" icon="ant-design:edit-outlined"
@click="editHandleClick" />
</a-tooltip>
</div>
<div v-else>
<a-input ref="accountNameEdit" :maxlength="100" v-model:value="userInfo.realname" @blur="editRealName" />
</div>
<div class="use-day">
使用:<span>{{userInfo.createTimeText}}</span>
</div>
</div>
</div>
</div>
<div class="account-data">
<!-- 详细资料 -->
<div class="account-detail">
<div class="font-size-15 font-bold font-color-gray" style="margin-bottom: 16px">详细资料</div>
<div class="margin-bottom-10 font-size-13">
<span class="gray-75 item-label">生日</span>
<span class="gray-3">{{ userInfo.birthday }}</span>
</div>
<div class="margin-bottom-10 font-size-13">
<span class="gray-75 item-label">性别</span>
<span class="gray-3">{{ userInfo.sexText }}</span>
</div>
<div class="margin-bottom-10 nowarp font-size-13">
<span class="gray-75 item-label">职位</span>
<span class="gray-3">{{ userInfo.postText ? userInfo.postText : "未填写" }}</span>
</div>
<div class="font-size-13">
<span class="item-label"></span>
<span class="item-label pointer" style="color:#1e88e5" @click="openEditModal">编辑</span>
</div>
</div>
<!-- 联系信息 -->
<div class="account-info">
<div class="font-size-15 font-bold font-color-gray" style="margin-bottom: 16px">联系信息</div>
<div class="margin-bottom-10 font-size-13">
<span class="gray-75 item-label">邮箱</span>
<span class="gray-3">{{ userInfo.email ? userInfo.email : "未填写" }}</span>
</div>
<div class="margin-bottom-10 font-size-13">
<span class="gray-75 item-label">手机</span>
<span class="gray-3">{{ userInfo.phone ? userInfo.phone : "未填写" }}</span>
</div>
</div>
</div>
</div>
<UserAccountModal @register="registerModal" @success="getUserDetail"></UserAccountModal>
</template>
<script lang="ts" setup>
import { computed, onMounted, ref } from "vue";
import { CollapseContainer } from "/@/components/Container";
import { CropperAvatar } from "/@/components/Cropper";
import { useMessage } from "/@/hooks/web/useMessage";
import headerImg from "/@/assets/images/header.jpg";
import { defHttp } from "/@/utils/http/axios";
import { useUserStore } from "/@/store/modules/user";
import { uploadImg } from "/@/api/sys/upload";
import { getFileAccessHttpUrl } from "/@/utils/common/compUtils";
import dayjs from "dayjs";
import { ajaxGetDictItems, getDictItemsByCode, initDictOptions } from "/@/utils/dict";
import { userEdit, getUserData, queryNameByCodes } from "./UserSetting.api";
import UserAccountModal from "./commponents/UserAccountModal.vue";
import { useModal } from "/@/components/Modal";
import { cloneDeep } from "lodash-es";
//TODO 当字典租户隔离时,数据会查不到,默认一个
const sexOption = getDictItemsByCode("sex") || [{text:'男',value:'1'},{text:'女',value:'2'}];
const { createMessage } = useMessage();
const userStore = useUserStore();
//是否编辑
const isEdit = ref<boolean>(false);
//用户信息
const userInfo = ref<any>({});
//编辑时input触发事件
const accountNameEdit = ref();
const [registerModal, { openModal }] = useModal();
//头像动态计算
const avatar = computed(() => {
return getFileAccessHttpUrl(userInfo.value.avatar) || headerImg;
});
/**
* 更新用户头像
*/
function updateAvatar(src: string, data: string) {
const userinfo = userStore.getUserInfo;
userinfo.avatar = data;
userStore.setUserInfo(userinfo);
if (data) {
updateUserInfo({ avatar: data, id: userinfo.id });
}
}
/**
* 更新用户信息
* @params 参数
*/
function updateUserInfo(params) {
userEdit(params).then((res) => {
if (!res.success) {
createMessage.warn(res.message);
}
});
}
/**
* 编辑按钮点击事件
*/
function editHandleClick() {
isEdit.value = true;
setTimeout(() => {
accountNameEdit.value.focus();
}, 100);
}
/**
* 修改真实姓名
*/
function editRealName() {
if (userInfo.value.realname) {
updateUserInfo({ realname: userInfo.value.realname, id: userInfo.value.id });
userStore.setUserInfo(userInfo.value);
} else {
createMessage.warn("请输入姓名");
}
isEdit.value = false;
}
/**
* 获取生日信息
*/
function getBirthDay(val) {
if (val) {
return dayjs(val).format("YYYY-MM-DD");
} else {
return "未填写";
}
}
/**
* 获取性别
* @param val
*/
function getSex(val) {
let findOption = sexOption.find(item => parseInt(item.value) === val);
let sex = "未填写";
if (findOption) {
sex = findOption.text;
}
return sex;
}
/**
* 打开编辑弹窗
*/
function openEditModal() {
let value = cloneDeep(userInfo.value);
openModal(true, {
record: value
});
}
/**
* 获取用户信息
*/
function getUserDetail() {
getUserData().then((async res => {
if (res.success) {
if (res.result) {
res.result.sexText = getSex(res.result.sex);
res.result.birthday = getBirthDay(res.result.birthday);
res.result.createTimeText = getDiffDay(res.result.createTime);
userInfo.value = res.result;
} else {
userInfo.value = {};
}
}
}));
}
/**
* 获取使用时间
* @param date
*/
function getDiffDay(date) {
// 计算两个日期之间的天数差值
let totalDays, diffDate
let createDate = Date.parse(date);
let nowDate = new Date().getTime();
// 将两个日期都转换为毫秒格式,然后做差
diffDate = Math.abs(nowDate - createDate) // 取相差毫秒数的绝对值
totalDays = Math.floor(diffDate / (1000 * 3600 * 24)) // 向下取整
return totalDays+" 天";
}
onMounted(async () => {
getUserDetail();
});
</script>
<style lang="less" scoped>
.user-setting-top {
padding-top: 40px;
width: 100%;
border-bottom: 1px solid @border-color-base;
display: flex;
padding-bottom: 40px;
}
.account-avatar {
align-items: center;
display: flex;
margin-right: 30px;
flex: 1;
}
.change-avatar {
img {
display: block;
margin-bottom: 15px;
border-radius: 50%;
}
}
.account-right {
margin-left: 25px !important;
}
.font-size-15 {
font-size: 15px;
}
.font-size-17 {
font-size: 17px;
}
.pointer {
cursor: pointer;
}
.account-name {
white-space: nowrap;
overflow: hidden;
width: 200px;
text-overflow: ellipsis;
line-height: 32px!important;
/*begin 兼容暗夜模式*/
color: @text-color;
/*end 兼容暗夜模式*/
font-weight: 500;
}
.gray-bd {
color: #bdbdbd
}
.account-icon {
margin-left: 4px;
}
.account-data {
width: 100% !important;
display: flex;
min-width: 0;
}
.account-detail {
width: 40%;
display: flex;
flex-direction: column;
padding: 40px 0;
.item-label {
display: inline-block;
text-align: left;
width: 80px;
}
}
.font-bold {
font-weight: 700 !important;
}
.margin-bottom-10 {
margin-bottom: 10px;
}
/*begin 兼容暗夜模式*/
.gray-75 {
color: @text-color !important;
}
.gray-3 {
color: @text-color;
}
/*end 兼容暗夜模式*/
.account-info {
width: 60%;
display: flex;
flex-direction: column;
padding: 40px 0;
box-sizing: border-box;
margin-left: 10px;
.item-label {
display: inline-block;
text-align: left;
width: 80px;
}
}
.nowarp {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.account-padding{
padding-left: 20px !important;
padding-right: 40px !important;
}
.use-day{
/*begin 兼容暗夜模式*/
color: @text-color;
/*end 兼容暗夜模式*/
margin-top: 10px;
font-size: 13px;
span{
color:#1e88e5;
margin-left: 5px;
}
}
.font-size-13{
font-size: 13px;
}
/*begin 兼容暗夜模式*/
.font-color-gray{
color: @text-color;
}
/*end 兼容暗夜模式*/
</style>
@@ -0,0 +1,581 @@
<template>
<div class="tenant-padding">
<div class="my-tenant">我的租户</div>
<div class="tenant-list" v-if="dataSource.length>0">
<div v-for="item in dataSource" class="tenant-list-item" @click="drownClick(item)">
<div class="tenant-title">
<div class="item-left">
<div class="item-name">{{ item.name }}</div>
<div class="item-house" @click.stop="copyClick(item.houseNumber)">
<span>
组织门牌号:{{ item.houseNumber }}
<Icon icon="ant-design:copy-outlined" style="font-size: 13px; margin-left: 2px" />
</span>
</div>
</div>
<div class="item-right">
<span v-if="item.userTenantStatus === '3'">
<span class="examine">待审核</span>
<span class="pointer cancel-apply" @click.stop="cancelApplyClick(item.tenantUserId)">取消申请</span>
</span>
<div v-else style="width: 75px"></div>
<span style="margin-left: 24px">
<Icon v-if="item.show" icon="ant-design:down-outlined" style="font-size: 13px; color: #707070" />
<Icon v-else icon="ant-design:right-outlined" style="font-size: 13px; color: #707070" />
</span>
</div>
</div>
<div class="item-content" v-show="item.show">
<div class="content-box">
<div class="content-name"> 组织名片 </div>
<div class="content-desc">
<div class="flex-flow">
<div class="content-des-text">姓名</div>
<div style="font-size: 13px;color: #000000">
{{ userDetail.realname }}
</div>
</div>
<div class="flex-flow">
<div class="content-des-text">部门</div>
<div style="font-size: 13px">
{{ userDetail.orgCodeTxt ? userDetail.orgCodeTxt : '未填写' }}
</div>
</div>
<div class="flex-flow">
<div class="content-des-text">职业</div>
<div style="font-size: 13px">
{{ userDetail.postText ? userDetail.postText : '未填写' }}
</div>
</div>
</div>
</div>
<div class="footer-box">
<span
v-if="item.userTenantStatus !== '3'"
@click.stop="footerClick('editTenant', item)"
class="font-color333 flex-flow margin-right40 font-size13 pointer"
>
<Icon icon="ant-design:edit-outlined" class="footer-icon" />
<span>查看租户名片</span>
</span>
<span v-else class="font-color9e flex-flow margin-right40 font-size13">
<Icon icon="ant-design:edit-outlined" class="footer-icon" />
<span>查看租户名片</span>
</span>
<span
v-if="item.userTenantStatus !== '3' && item.auth"
@click.stop="footerClick('tenantSetting', item)"
class="font-color333 flex-flow margin-right40 font-size13 pointer"
>
<Icon icon="ant-design:tool-outlined" class="footer-icon" />
<span>租户管理?</span>
</span>
<span v-else-if="item.userTenantStatus === '3' && item.auth" class="font-color9e flex-flow margin-right40 font-size13">
<Icon icon="ant-design:tool-outlined" class="footer-icon" />
<span>租户管理?</span>
</span>
<span
v-if="item.userTenantStatus !== '3' && !item.auth"
@click.stop="footerClick('tenantSetting', item)"
class="font-color333 flex-flow margin-right40 font-size13 pointer"
>
<Icon icon="ant-design:tool-outlined" class="footer-icon" />
<span>申请角色权限?</span>
</span>
<span v-else-if="item.userTenantStatus === '3' && !item.auth" class="font-color9e flex-flow margin-right40 font-size13">
<Icon icon="ant-design:tool-outlined" class="footer-icon" />
<span>申请角色权限?</span>
</span>
<span
v-if="item.userTenantStatus !== '3'"
@click.stop="footerClick('tenantSetting', item)"
class="font-color333 flex-flow margin-right40 font-size13 pointer"
>
<Icon icon="ant-design:gold-outlined" class="footer-icon" />
<span>我的汇报关系?</span>
</span>
<span v-else class="font-color9e flex-flow margin-right40 font-size13">
<Icon icon="ant-design:gold-outlined" class="footer-icon" />
<span>我的汇报关系?</span>
</span>
<span
v-if="item.userTenantStatus !== '3'"
@click.stop="footerClick('exitTenant', item)"
class="font-color333 flex-flow margin-right40 font-size13 pointer"
>
<Icon icon="ant-design:export-outlined" class="footer-icon" />
<span>退出租户</span>
</span>
<span v-else class="font-color9e flex-flow margin-right40 font-size13">
<Icon icon="ant-design:export-outlined" class="footer-icon" />
<span>退出租户</span>
</span>
</div>
</div>
</div>
</div>
<a-empty v-else description="暂无数据" style="position: relative;top: 50px;"/>
</div>
<a-modal v-model:visible="tenantVisible" width="400px" wrapClassName="edit-tenant-setting">
<template #title>
<div style="font-size: 17px; font-weight: 700">查看名片</div>
<div style="color: #9e9e9e; margin-top: 10px; font-size: 13px"> 名片是您在该组织下的个人信息,只在本组织中展示。 </div>
</template>
<div style="margin-top: 24px; font-size: 13px; padding: 0 24px">
<div class="font-color75">姓名</div>
<div class="margin-top6 margin-bottom-16">{{ userDetail.realname }}</div>
<div>部门</div>
<div class="margin-top6 margin-bottom-16">{{ userDetail.orgCodeTxt ? userDetail.orgCodeTxt : '未填写' }}</div>
<div>职位</div>
<div class="margin-top6 margin-bottom-16">{{ userDetail.postText ? userDetail.postText : '未填写' }}</div>
<div>工作地点</div>
<div class="margin-top6 margin-bottom-16">{{ userData.workPlace ? userData.workPlace : '未填写' }}</div>
<div>工号</div>
<div class="margin-top6 margin-bottom-16">{{ userDetail.workNo ? userDetail.workNo : '未填写' }}</div>
</div>
</a-modal>
<!-- 退出租户 -->
<a-modal v-model:visible="cancelVisible" width="800" destroy-on-close>
<template #title>
<div class="cancellation">
<Icon icon="ant-design:warning-outlined" style="font-size: 20px;color: red"/>
退出租户 {{myTenantInfo.name}}
</div>
</template>
<a-form :model="formCancelState" ref="cancelTenantRef">
<a-form-item name="tenantName">
<a-row :span="24" style="padding: 20px 20px 0;font-size: 13px">
<a-col :span="24">
请输入租户名称
</a-col>
<a-col :span="24" style="margin-top: 10px">
<a-input v-model:value="formCancelState.tenantName" @change="tenantNameChange"/>
</a-col>
</a-row>
</a-form-item>
<a-form-item name="loginPassword">
<a-row :span="24" style="padding: 0 20px;font-size: 13px">
<a-col :span="24">
请输入您的登录密码
</a-col>
<a-col :span="24" style="margin-top: 10px">
<a-input-password v-model:value="formCancelState.loginPassword" />
</a-col>
</a-row>
</a-form-item>
</a-form>
<template #footer>
<a-button type="primary" @click="handleOutClick" :disabled="outBtnDisabled">确定</a-button>
<a-button @click="handleCancelOutClick">取消</a-button>
</template>
</a-modal>
<a-modal
title="变更拥有者"
v-model:visible="owenVisible"
width="800"
destroy-on-close
:cancelButtonProps="{display:'none'}"
@ok="changeOwen">
<div style="padding: 20px">
<a-row :span="24">
<div class="change-owen">
只有变更拥有着之后,才能退出
</div>
</a-row>
<a-row :span="24" style="margin-top: 10px">
<UserSelect v-model:value="tenantOwen" izExcludeMy/>
</a-row>
</div>
</a-modal>
</template>
<script lang="ts" name="tenant-setting" setup>
import { onMounted, ref, unref } from "vue";
import {getTenantListByUserId, cancelApplyTenant, exitUserTenant, changeOwenUserTenant} from "./UserSetting.api";
import { useUserStore } from "/@/store/modules/user";
import { CollapseContainer } from "/@/components/Container";
import { getFileAccessHttpUrl } from "/@/utils/common/compUtils";
import headerImg from "/@/assets/images/header.jpg";
import {useMessage} from "/@/hooks/web/useMessage";
import { initDictOptions } from '/@/utils/dict';
import { uniqWith } from 'lodash-es';
import { Modal } from 'ant-design-vue';
import UserSelect from '/@/components/Form/src/jeecg/components/userSelect/index.vue';
//数据源
const dataSource = ref<any>([]);
const userStore = useUserStore();
//数据源
const { createMessage } = useMessage();
//部门字典
const departOptions = ref<any>([]);
//租户编辑是或否隐藏
const tenantVisible = ref<boolean>(false);
//用户数据
const userData = ref<any>([]);
//用户
const userDetail = ref({
realname: userStore.getUserInfo.realname,
workNo: userStore.getUserInfo.workNo,
orgCodeTxt: userStore.getUserInfo.orgCodeTxt,
postText: userStore.getUserInfo.postText,
});
/**
* 初始化租户数据
*/
async function initDataSource() {
//获取用户数据
//update-begin---author:wangshuai ---date:20230109 for: [QQYUN-3645]个人设置我的租户查询审核中和正常的------------
getTenantListByUserId({ userTenantStatus: '1,3' }).then((res) => {
if (res.success) {
dataSource.value = res.result;
} else {
dataSource.value = [];
}
});
//update-end---author:wangshuai ---date:20230109 for:[QQYUN-3645]个人设置我的租户查询审核中和正常的------------
}
/**
* 复制门户
* @param value
*/
function copyClick(value) {
// 创建input元素
const el = document.createElement('input');
// 给input元素赋值需要复制的文本
el.setAttribute('value', value);
// 将input元素插入页面
document.body.appendChild(el);
// 选中input元素的文本
el.select();
// 复制内容到剪贴板
document.execCommand('copy');
// 删除input元素
document.body.removeChild(el);
createMessage.success('复制成功');
};
/**
* 取消申请
* @param id
*/
function cancelApplyClick(id) {
Modal.confirm({
title: '取消申请',
content: '是否取消申请',
okText: '确认',
cancelText: '取消',
onOk: () => {
cancelApplyTenant({ tenantId: id }).then((res) => {
if (res.success) {
createMessage.success('取消申请成功');
initDataSource();
}else{
createMessage.warning(res.message);
}
}).catch((e)=>{
createMessage.warning(e.message);
});
},
});
};
/**
* 展开关闭事件
*/
function drownClick(value) {
if (!value.show) {
value.show = true;
} else {
value.show = false;
}
};
/**
* 获取部门文本
* @param value
*/
function getDepartText(value) {
let arr = departOptions.value.filter((item) => {
item.value == value;
});
if (arr && arr.length > 0) {
return arr[0].label;
}
return '未填写';
};
/**
* 底部文本点击事件
*/
function footerClick(type, item) {
userData.value = item;
//编辑组织名片
if (type === 'editTenant') {
tenantVisible.value = true;
}else if(type === 'exitTenant'){
//退出租户
formCancelState.value = {loginPassword:'', tenantName:''};
outBtnDisabled.value = true;
cancelVisible.value = true;
myTenantInfo.value = item;
}
}
//退出租户弹窗
const cancelVisible = ref<boolean>(false);
//退出租户数据
const formCancelState = ref<any>({});
//租户数据
const myTenantInfo = ref<any>({});
//注销租户弹窗确定按钮是否可以点击
const outBtnDisabled = ref<boolean>(true);
//拥有者
const tenantOwen = ref<string>('');
//拥有者弹窗
const owenVisible = ref<boolean>(false);
/**
* 租户名称值改变事件
*/
function tenantNameChange() {
let name = unref(myTenantInfo).name;
let tenantName = unref(formCancelState).tenantName;
if(name === tenantName){
outBtnDisabled.value = false;
}else{
outBtnDisabled.value = true;
}
}
/**
* 退出确定点击事件
*/
async function handleOutClick() {
if(!unref(formCancelState).loginPassword){
createMessage.warning("请输入登录密码");
return;
}
console.log("myTenantInfo::::",myTenantInfo);
await exitUserTenant({ id: unref(myTenantInfo).tenantUserId, loginPassword: unref(formCancelState).loginPassword }).then((res) => {
if (res.success) {
createMessage.success(res.message);
cancelVisible.value = false;
//切换租户后要刷新首页
window.location.reload();
} else {
if (res.message === 'assignedOwen') {
//需要指定变更者
owenVisible.value = true;
cancelVisible.value = false;
} else {
createMessage.warning(res.message);
}
}
}).catch((res) => {
createMessage.warning(res.message);
})
}
/**
* 退出租户取消事件
*/
function handleCancelOutClick() {
cancelVisible.value = false;
outBtnDisabled.value = true;
}
/**
* 变更拥有着
*/
function changeOwen() {
if(!unref(tenantOwen)){
createMessage.warning("请选择变更拥有者");
return;
}
changeOwenUserTenant({userId:unref(tenantOwen)}).then((res) =>{
if(res.success){
createMessage.success(res.message);
//切换租户后要刷新首页
window.location.reload();
} else {
createMessage.warning(res.message);
}
})
}
onMounted(() => {
initDataSource();
});
</script>
<style lang="less" scoped>
.tenant-padding{
padding: 30px 40px 0 20px;
}
.my-tenant{
font-size: 17px;
font-weight: 700!important;
/*begin 兼容暗夜模式*/
color: @text-color;
/*end 兼容暗夜模式*/
margin-bottom: 20px;
}
.tenant-list{
box-sizing: border-box;
flex: 1;
min-height: 0;
overflow-x: hidden;
}
.tenant-list-item{
/*begin 兼容暗夜模式*/
border: 1px solid @border-color-base;
/*end 兼容暗夜模式*/
box-sizing: border-box;
display: flex;
flex-direction: column;
margin-bottom: 20px;
overflow: hidden;
padding: 0 25px;
width: 100%;
.item-name{
align-items: center;
box-sizing: border-box;
display: flex;
justify-content: space-between;
padding: 14px 0;
cursor: pointer;
font-size:17px;
/*begin 兼容暗夜模式*/
color: @text-color;
/*end 兼容暗夜模式*/
font-weight: 700!important;
}
}
.tenant-list-item:hover{
box-shadow: 0 1px 2px 0 rgba(0,0,0,0.2);
}
.pointer {
cursor: pointer;
}
.examine {
color: #2c9cff;
font-size: 13px;
}
.cancel-apply {
margin-left: 24px;
color: red;
font-size: 13px;
}
.item-content {
transition: ease-in 2s;
.content-box {
/*begin 兼容暗夜模式*/
border-top: 1px solid @border-color-base;
/*end 兼容暗夜模式*/
box-sizing: border-box;
display: flex;
padding: 24px 0;
}
.content-name {
/*begin 兼容暗夜模式*/
color: @text-color;
/*end 兼容暗夜模式*/
text-align: center;
width: 100px;
font-size: 13px;
}
.content-desc {
flex: 1;
min-width: 0;
}
.content-des-text {
/*begin 兼容暗夜模式*/
color: @text-color;
/*end 兼容暗夜模式*/
text-align: left;
width: 76px;
font-size: 13px;
}
}
.flex-flow {
display: flex;
min-width: 0;
}
.footer-box {
/*begin 兼容暗夜模式*/
border-top: 1px solid @border-color-base;
/*end 兼容暗夜模式*/
box-sizing: border-box;
display: flex;
padding: 24px 0;
color: #757575;
}
.margin-right40 {
margin-right: 40px;
}
/*begin 兼容暗夜模式*/
.font-color333 {
color: @text-color;
font-weight: normal;
}
.font-color9e {
color: @text-color;
}
.font-color75 {
color: @text-color;
}
/*end 兼容暗夜模式*/
.font-size13 {
font-size: 13px;
}
.footer-icon {
font-size: 13px !important;
margin-right: 13px;
position: relative;
top: 4px;
}
:deep(.edit-tenant-setting) {
color: #0a8fe9;
}
.margin-top6 {
margin-top: 6px;
}
.margin-bottom-16 {
margin-bottom: 16px;
}
.item-right {
align-items: center;
display: flex;
}
.tenant-title {
align-items: center;
box-sizing: border-box;
display: flex;
justify-content: space-between;
padding: 24px 0;
}
.change-owen{
font-size: 14px;
font-weight: 700;
}
</style>
@@ -0,0 +1,85 @@
import { defHttp } from "/@/utils/http/axios";
enum Api {
userEdit='/sys/user/login/setting/userEdit',
getUserData='/sys/user/login/setting/getUserData',
queryNameByCodes='/sys/position/queryByCodes',
updateMobile='/sys/user/updateMobile',
updateUserPassword='/sys/user/updatePassword',
getTenantListByUserId = '/sys/tenant/getTenantListByUserId',
cancelApplyTenant = '/sys/tenant/cancelApplyTenant',
exitUserTenant = '/sys/tenant/exitUserTenant',
changeOwenUserTenant = '/sys/tenant/changeOwenUserTenant',
}
/**
* 用户编辑
* @param params
*/
export const userEdit = (params) => {
return defHttp.post({ url: Api.userEdit, params },{ isTransformResponse:false });
}
/**
* 获取用户信息
* @param params
*/
export const getUserData = () => {
return defHttp.get({ url: Api.getUserData },{ isTransformResponse:false });
}
/**
* 获取多个职务信息
* @param params
*/
export const queryNameByCodes = (params) => {
return defHttp.get({ url: Api.queryNameByCodes, params },{isTransformResponse:false});
}
/**
* 修改手机号
* @param params
*/
export const updateMobile = (params) => {
return defHttp.put({ url: Api.updateMobile, params },{isTransformResponse:false});
}
/**
* 修改密码
* @param params
*/
export const updateUserPassword = (params) => {
return defHttp.put({ url: Api.updateUserPassword, params },{isTransformResponse:false});
}
/**
* 通过用户id获取租户列表
* @param params
*/
export const getTenantListByUserId = (params) => {
return defHttp.get({ url: Api.getTenantListByUserId, params }, { isTransformResponse: false });
};
/**
* 取消申请
* @param params
*/
export const cancelApplyTenant = (params) => {
return defHttp.put({ url: Api.cancelApplyTenant, data: params }, { joinParamsToUrl: true, isTransformResponse: false });
};
/**
* 用户退出租户
* @param params
*/
export const exitUserTenant = (params)=>{
return defHttp.delete({ url: Api.exitUserTenant, params },{ isTransformResponse: false, joinParamsToUrl: true });
}
/**
* 变更租户拥有者
* @param params
*/
export const changeOwenUserTenant = (params)=>{
return defHttp.post({ url: Api.changeOwenUserTenant, params },{ isTransformResponse: false, joinParamsToUrl: true });
}
@@ -0,0 +1,146 @@
import { FormSchema } from '/@/components/Form/index';
import { rules } from '/@/utils/helper/validator';
export interface ListItem {
key: string;
title: string;
description: string;
extra?: string;
avatar?: string;
color?: string;
}
// tab的list
export const settingList = [
{
key: '1',
name: '个人信息',
component: 'BaseSetting',
icon:'ant-design:user-outlined'
},
{
key: '2',
name: '我的租户',
component: 'TenantSetting',
icon:'ant-design:team-outlined'
},
{
key: '3',
name: '账号安全',
component: 'AccountSetting',
icon:'ant-design:lock-outlined'
},
{
key: '4',
name: '第三方APP',
component: 'WeChatDingSetting',
icon: 'ant-design:contacts-outlined',
},
];
/**
* 用户表单
*/
export const formSchema: FormSchema[] = [
{
field: 'realname',
component: 'Input',
label: '姓名',
colProps: { span: 24 },
required:true
},
{
field: 'birthday',
component: 'DatePicker',
label: '生日',
colProps: { span: 24 },
componentProps:{
showTime:false,
valueFormat:"YYYY-MM-DD",
getPopupContainer: () => document.body,
},
},
{
field: 'sex',
component: 'RadioGroup',
label: '性别',
colProps: { span: 24 },
componentProps:{
options: [
{
label: '男',
value: 1,
},
{
label: '女',
value: 2,
},
],
}
},
{
field: 'relTenantIds',
component: 'JDictSelectTag',
label: '租户',
colProps: { span: 24 },
componentProps:{
mode:'multiple',
dictCode:'sys_tenant,name,id',
disabled:true
}
},
{
field: 'post',
component: 'JDictSelectTag',
label: '职位',
colProps: { span: 24 },
componentProps:{
mode:'multiple',
dictCode:'sys_position,name,code',
disabled:true
}
},
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
]
//密码弹窗
export const formPasswordSchema: FormSchema[] = [
{
label: '用户账号',
field: 'username',
component: 'Input',
componentProps: { readOnly: true },
},
{
label: '旧密码',
field: 'oldpassword',
component: 'InputPassword',
required: true,
},
{
label: '新密码',
field: 'password',
component: 'StrengthMeter',
componentProps: {
placeholder: '请输入新密码',
},
rules: [
{
required: true,
message: '请输入新密码',
},
],
},
{
label: '确认新密码',
field: 'confirmpassword',
component: 'InputPassword',
dynamicRules: ({ values }) => rules.confirmPassword(values, true),
},
];
@@ -0,0 +1,116 @@
<template>
<ScrollContainer>
<div ref="wrapperRef" :class="prefixCls">
<Tabs tab-position="left" :tabBarStyle="tabBarStyle" @tabClick="componentClick">
<template v-for="item in settingList" :key="item.key">
<TabPane>
<template #tab>
<span>
<Icon :icon="item.icon" class="icon-font-color"/>
{{item.name}}
</span>
</template>
<component :is="item.component" v-if="activeKey === item.key" />
</TabPane>
</template>
</Tabs>
</div>
</ScrollContainer>
</template>
<script lang="ts">
import { ref, defineComponent } from "vue";
import { Tabs } from "ant-design-vue";
import { ScrollContainer } from "/@/components/Container";
import { settingList } from "./UserSetting.data";
import BaseSetting from "./BaseSetting.vue";
import AccountSetting from "./AccountSetting.vue";
import TenantSetting from "./TenantSetting.vue";
import WeChatDingSetting from './WeChatDingSetting.vue';
export default defineComponent({
components: {
ScrollContainer,
Tabs,
TabPane: Tabs.TabPane,
BaseSetting,
AccountSetting,
TenantSetting,
WeChatDingSetting,
},
setup() {
const activeKey = ref<string>('1');
/**
* 组件标题点击事件,解决第二次不加载数据
* @param key
*/
function componentClick(key) {
activeKey.value = key;
}
return {
prefixCls: "account-setting",
settingList,
tabBarStyle: {
width: "220px",
marginBottom: "200px"
},
componentClick,
activeKey,
};
}
});
</script>
<style lang="less" scoped>
.account-setting {
margin: 12px;
.base-title {
padding-left: 0;
}
.ant-tabs-tab-active {
background-color: @item-active-bg;
}
//tabs弹窗左边样式
:deep(.ant-tabs-nav){
/*begin 兼容暗夜模式*/
background-color: @component-background;
/*end 兼容暗夜模式*/
height: 260px;
}
//tabs弹窗右边边样式
:deep(.ant-tabs-content-holder){
position: relative;
left: 12px;
/*begin 兼容暗夜模式*/
background: @component-background;
/*end 兼容暗夜模式*/
height: auto !important;
}
}
//tab点击样式
:deep(.ant-tabs-tab-active){
border-radius: 0 20px 20px 0;
background-color: #1294f7!important;
color: #fff!important;
.icon-font-color{
color: #fff;
}
}
:deep(.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn){
color: white !important;
}
:deep(.ant-tabs-ink-bar){
visibility: hidden;
}
:deep(.ant-tabs-nav-list){
padding-top:14px;
padding-right:14px;
}
.icon-font-color{
/*begin 兼容暗夜模式*/
color: @text-color;
/*end 兼容暗夜模式*/
}
</style>
@@ -0,0 +1,116 @@
<template>
<div class="account-padding">
<div class="my-account">第三方APP</div>
<div class="account-row-item">
<div class="account-label gray-75">企业微信绑定</div>
<span>
<icon-font :style="!bindData.bindWechat ? { color: '#9e9e9e' } : { color: '#1ec563' }" class="item-icon" type="icon-qiyeweixin3" />
<span class="gray-75" style="margin-left: 12px">企业微信</span>
<span class="gray-75" style="margin-left: 8px" v-if="bindData.bindWechat">{{ '已绑定:' + bindData.weChatName }}</span>
<span class="blue-e5 pointer" style="margin-left: 24px" @click="wechatBind">{{ !bindData.bindWechat ? '绑定' : '解绑' }}</span>
</span>
</div>
<div class="account-row-item">
<div class="account-label gray-75">钉钉绑定</div>
<span>
<DingtalkCircleFilled :style="!bindData.bindDing ? { color: '#9e9e9e' } : { color: '#1ec563' }" class="item-icon" />
<span class="gray-75" style="margin-left: 12px">钉钉</span>
<span class="gray-75" style="margin-left: 8px" v-if="bindData.bindDing">{{ '已绑定:' + bindData.dingName }}</span>
<span class="blue-e5 pointer" style="margin-left: 24px" @click="dingDingBind">{{ !bindData.bindDing ? '绑定' : '解绑' }}</span>
</span>
</div>
</div>
</template>
<script lang="ts" setup name="we-chat-ding-setting">
import { onMounted, ref, reactive } from 'vue';
import { CollapseContainer } from '/@/components/Container';
import { getUserData } from './UserSetting.api';
import { useUserStore } from '/@/store/modules/user';
import { useModal } from '/@/components/Modal';
import { DingtalkCircleFilled, createFromIconfontCN } from '@ant-design/icons-vue';
const IconFont = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_2316098_umqusozousr.js',
});
const userDetail = ref<any>([]);
const userStore = useUserStore();
const bindData = reactive<any>({
bindWechat: false,
weChatName: '昵称',
bindDing: false,
dingName: '昵称',
});
/**
* 初始化钉钉和企业微信数据
*/
function initUserDetail() {}
/**
* 微信绑定解绑事件
*/
function wechatBind() {
console.log('微信绑定解绑事件');
}
/**
* 钉钉绑定解绑事件
*/
function dingDingBind() {
console.log('钉钉绑定解绑事件');
}
onMounted(() => {
initUserDetail();
});
</script>
<style lang="less" scoped>
.account-row-item {
align-items: center;
border-bottom: 1px solid #eaeaea;
box-sizing: border-box;
display: flex;
height: 71px;
position: relative;
}
.account-label {
text-align: left;
width: 160px;
}
.gray-75 {
color: #757575 !important;
}
.pointer {
cursor: pointer;
}
.blue-e5 {
color: #1e88e5;
}
.phone-margin {
margin-left: 24px;
margin-right: 24px;
}
.clearfix:after {
clear: both;
}
.clearfix:before {
content: '';
display: table;
}
.account-padding {
padding: 30px 40px 0 20px;
}
.my-account {
font-size: 17px;
font-weight: 700 !important;
color: #333 !important;
margin-bottom: 20px;
}
</style>
@@ -0,0 +1,70 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" width="500px" :title="title" @ok="handleSubmit" destroyOnClose>
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form';
import { formSchema } from '../UserSetting.data';
import { ref, unref, defineEmits } from 'vue';
import { userEdit } from "../UserSetting.api";
import { useUserStore } from "/@/store/modules/user";
import { useMessage } from "/@/hooks/web/useMessage";
const userStore = useUserStore();
const { createMessage } = useMessage();
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
schemas: formSchema,
showActionButtonGroup: false,
});
const userDetail = ref<any>({});
const isUpdate = ref<boolean>(false);
const title = ref<string>('');
const emit = defineEmits(['register','success']);
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
await resetFields();
setModalProps({ confirmLoading: false });
title.value = '编辑个人资料';
if(data.record.post){
data.record.post = data.record.post.split(",")
}
if(data.record.relTenantIds){
data.record.relTenantIds = data.record.relTenantIds.split(",")
}
userDetail.value = data.record;
//update-begin---author:wangshuai ---date:20230130 for:[QQYUN-3976]个人信息 编辑时,刚注册没有设置生日 生日显示有问题------------
if(data.record.birthday === '未填写'){
data.record.birthday = undefined;
}
//update-end---author:wangshuai ---date:20230130 for:[QQYUN-3976]个人信息 编辑时,刚注册没有设置生日 生日显示有问题------------
await setFieldsValue({ ...data.record });
});
/**
* 数据修改和新增
*/
async function handleSubmit() {
try {
let values = await validate();
setModalProps({ confirmLoading: true });
//提交表单
await userEdit(values).then((res) =>{
if(res.success){
createMessage.success(res.message)
}else{
createMessage.warn(res.message)
}
});
//更新缓存
Object.assign(userDetail.value,values)
userStore.setUserInfo(unref(userDetail));
emit('success')
//关闭弹窗
closeModal();
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>
@@ -0,0 +1,80 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" width="500px" :title="title" :showCancelBtn="false" :showOkBtn="false">
<a-form class="antd-modal-form" ref="formRef" :model="formState" :rules="validatorRules">
<a-form-item name="email">
<a-input size="large" v-model:value="formState.email" placeholder="请输入邮箱" />
</a-form-item>
<a-form-item>
<a-button size="large" type="primary" block @click="updateEmail">
确认
</a-button>
</a-form-item>
</a-form>
</BasicModal>
</template>
<script lang="ts" setup name="user-replace-email-modal">
import BasicModal from "/@/components/Modal/src/BasicModal.vue";
import { CountdownInput } from '/@/components/CountDown';
import { useUserStore } from "/@/store/modules/user";
import { useMessage } from "/@/hooks/web/useMessage";
import { defineEmits, ref, reactive, toRaw } from "vue";
import { useModalInner } from "/@/components/Modal";
import { getCaptcha } from "/@/api/sys/user";
import { SmsEnum } from "/@/views/sys/login/useLogin";
import { Rule } from "/@/components/Form";
import { rules } from "/@/utils/helper/validator";
import { Form } from "ant-design-vue";
import { updateMobile, userEdit } from "../UserSetting.api";
import { duplicateCheck } from "/@/views/system/user/user.api";
const userStore = useUserStore();
const { createMessage } = useMessage();
const formState = reactive<Record<string, any>>({
email:'',
});
const formRef = ref();
const validatorRules: Record<string, Rule[]> = {
email: [{...rules.duplicateCheckRule("sys_user",'email',formState,{ label:'邮箱' })[0]},{ required: true, type: 'email', message: '邮箱格式不正确' }],
};
const useForm = Form.useForm;
const title = ref<string>('');
const emit = defineEmits(['register','success']);
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
formRef.value.resetFields();
formRef.value.clearValidate();
setModalProps({ confirmLoading: false });
title.value = '修改邮箱';
//赋值
data.record.smscode = '';
Object.assign(formState, data.record);
});
/**
* 倒计时执行前的函数
*/
function sendCodeApi() {
return getCaptcha({ mobile: formState.phone, smsmode: SmsEnum.REGISTER });
}
/**
* 更新手机号
*/
async function updateEmail() {
await formRef.value.validateFields();
userEdit(formState).then((res) =>{
if(res.success){
createMessage.success("修改邮箱成功")
emit("success")
closeModal();
}else{
createMessage.warning(res.message)
}
})
}
</script>
<style scoped>
</style>
@@ -0,0 +1,114 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" title="修改密码" @ok="handleSubmit">
<a-form class="antd-modal-form" ref="formRef" :model="formState" :rules="validatorRules">
<a-form-item name="oldpassword">
<div class="black font-size-13">原有密码</div>
<div class="pass-padding">
<a-input-password v-model:value="formState.oldpassword" placeholder="原有密码" />
</div>
<div style="display: block">
<span class="gray-9e float-left font-size-13">进入网站的登录密码</span>
</div>
</a-form-item>
<a-form-item name="password">
<span class="black font-size-13">新密码</span>
<div class="pass-padding">
<a-input-password v-model:value="formState.password" placeholder="新密码" />
</div>
<span class="gray-9e font-size-13">8-20位,需包含字母和数字</span>
</a-form-item>
</a-form>
</BasicModal>
</template>
<script lang="ts" name="user-pass-word-modal" setup>
import { ref, computed, unref, reactive } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { Rule } from '/@/components/Form/index';
import { updateUserPassword } from '../UserSetting.api';
import { useMessage } from "/@/hooks/web/useMessage";
import { useUserStore, useUserStoreWithOut } from "/@/store/modules/user";
const $message = useMessage();
//用户名
const username = ref<string>('')
const formRef = ref();
const formState = reactive({
oldpassword:'',
password:'',
});
// 声明Emits
const emit = defineEmits(['success', 'register']);
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({ confirmLoading: false });
username.value = data.record.username
});
const userStore = useUserStore();
const validatorRules: Record<string, Rule[]> = {
password: [{ required: true, validator:checkPassword},{ pattern:/^(?=.*[0-9])(?=.*[a-zA-Z])(.{8,20})$/,message:'8-20位,需包含字母和数字'}],
oldpassword: [{ required: true, message: '请输入原有密码' }],
};
//表单提交事件
async function handleSubmit() {
try {
let values = await formRef.value.validateFields();
setModalProps({ confirmLoading: true });
//提交表单
values.username = unref(username);
values.confirmpassword = values.password ;
await updateUserPassword(values).then((res) =>{
if(res.success){
$message.createMessage.info({
content:'密码修改成功,请重新登录!3s后自动退出登录',
duration: 3
})
//3s后返回登录页面
setTimeout(()=>{
userStore.logout(true);
},3000)
//关闭弹窗
closeModal();
}else{
$message.createMessage.warn(res.message);
}
});
} finally {
setModalProps({ confirmLoading: false });
}
}
/**
* 验证新密码是否为空
*/
function checkPassword(_rule: Rule, value: string) {
if(value === ''){
return Promise.reject('请输入新密码');
}
return Promise.resolve();
}
</script>
<style lang="less" scoped>
.black {
color: #000000;
}
.font-size-13 {
font-size: 13px;
line-height: 15px;
}
.gray-9e {
color: #9e9e9e;
}
.float-left {
float: left;
}
.pass-padding {
padding-top: 10px;
padding-bottom: 10px;
}
.forget{
float: right;
color: #1e88e5!important;
cursor: pointer
}
</style>
@@ -0,0 +1,83 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" width="500px" :title="title" :showCancelBtn="false" :showOkBtn="false">
<a-form class="antd-modal-form" ref="formRef" :model="formState" :rules="validatorRules">
<a-form-item name="phone">
<a-input size="large" v-model:value="formState.phone" placeholder="请输入手机号" />
</a-form-item>
<a-form-item name="smscode">
<CountdownInput size="large" v-model:value="formState.smscode" placeholder="输入6位验证码" :sendCodeApi="sendCodeApi" />
</a-form-item>
<a-form-item>
<a-button size="large" type="primary" block @click="updatePhone">
确认
</a-button>
</a-form-item>
</a-form>
</BasicModal>
</template>
<script lang="ts" setup name="user-replace-phone-modal">
import BasicModal from "/@/components/Modal/src/BasicModal.vue";
import { CountdownInput } from '/@/components/CountDown';
import { useUserStore } from "/@/store/modules/user";
import { useMessage } from "/@/hooks/web/useMessage";
import { defineEmits, ref, reactive, toRaw } from "vue";
import { useModalInner } from "/@/components/Modal";
import { getCaptcha } from "/@/api/sys/user";
import { SmsEnum } from "/@/views/sys/login/useLogin";
import { Rule } from "/@/components/Form";
import { rules } from "/@/utils/helper/validator";
import { Form } from "ant-design-vue";
import { updateMobile } from "../UserSetting.api";
import { duplicateCheck } from "/@/views/system/user/user.api";
const userStore = useUserStore();
const { createMessage } = useMessage();
const formState = reactive<Record<string, any>>({
phone:'',
smscode:''
});
const formRef = ref();
const userData = ref<any>({})
const validatorRules: Record<string, Rule[]> = {
phone: [{...rules.duplicateCheckRule("sys_user",'phone',formState,{ label:'手机号' })[0]},{ pattern: /^1[3456789]\d{9}$/, message: '手机号码格式有误' }],
smscode: [{ required: true,message:'请输入验证码' }],
};
const useForm = Form.useForm;
const title = ref<string>('');
const emit = defineEmits(['register','success']);
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
formRef.value.resetFields();
formRef.value.clearValidate();
setModalProps({ confirmLoading: false });
title.value = '修改手机号';
//赋值
data.record.smscode = '';
Object.assign(formState, data.record);
userData.value = data.record;
});
/**
* 倒计时执行前的函数
*/
function sendCodeApi() {
return getCaptcha({ mobile: formState.phone, smsmode: SmsEnum.REGISTER });
}
/**
* 更新手机号
*/
async function updatePhone() {
await formRef.value.validateFields();
updateMobile(formState).then((res) =>{
if(res.success){
createMessage.success("修改手机号成功")
emit("success")
closeModal();
}else{
createMessage.warning(res.message)
}
})
}
</script>