Commit a37e1465 authored by youjie's avatar youjie

旅客信息

parent 97df7b02
......@@ -211,6 +211,11 @@ export default {
changePhoto: '修改头像',
photo: '头像',
photoTip: '*支持jpg,gif,png格式图片,且文件小于2M。',
cancel: '取消',
addPassenger: '新增旅客',
editPassenger: '修改旅客',
deleteSuccess: '删除成功',
idCard: '身份证号',
},
// HTTP 错误状态码
httpError: {
......
......@@ -705,6 +705,55 @@ class UserService {
)
return response as unknown as HttpResponse
}
/**
* 旅客列表
* @param tenantId 租户ID(可选)
*/
static async memberGuest(tenantId: string,data:any): Promise<HttpResponse> {
const response = await OtaRequest.get(
'/member-guest/paged',
data,
{
headers: tenantId ? {
'__tenant': tenantId
} : {}
}
)
return response as unknown as HttpResponse
}
/**
* 新增修改旅客
* @param tenantId 租户ID(可选)
*/
static async updateMemberGuest(tenantId: string,data:any): Promise<HttpResponse> {
const response = await OtaRequest.post(
'/member-guest/or-update',
data,
{
headers: tenantId ? {
'__tenant': tenantId
} : {}
}
)
return response as unknown as HttpResponse
}
/**
* 删除旅客
* @param tenantId 租户ID(可选)
*/
static async deleteMemberGuest(tenantId: string,id:any): Promise<HttpResponse> {
const response = await OtaRequest.delete(
`/member-guest/${id}`,
{},
{
headers: tenantId ? {
'__tenant': tenantId
} : {}
}
)
return response as unknown as HttpResponse
}
}
export default UserService
<template>
<div class="w-[977px] h-full flex flex-col flex-shrink-0 overflow-hidden">
<div class="w-[977px] h-full flex flex-col flex-shrink-0"
:class="[current!=3?'overflow-hidden':'']">
<a-spin :loading="loading">
<div class="flex">
<div v-for="(item,index) in TitleBars"
......@@ -12,49 +13,29 @@
</div>
</div>
<a-divider class="!m-[0]"/>
<a-scrollbar class="max-h-[735px] overflow-auto mt-[20px]"
ref="scrollContainer">
<a-scrollbar class="max-h-[735px] mt-[20px]"
ref="scrollContainer"
:class="[current!=3?'overflow-auto':'']">
<basicInfor v-if="current==1"></basicInfor>
<account v-if="current==2"></account>
<passengerList v-if="current==3"></passengerList>
</a-scrollbar>
</a-spin>
</div>
</template>
<script setup lang="ts">
import { ref,reactive, onMounted, watch } from 'vue'
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useUserStore } from '@/stores/user'
import { useSystemConfigStore } from '@/stores/index'
import { Message } from '@arco-design/web-vue'
import { query } from '@/utils/common'
import { useRouter } from 'vue-router'
import UserService from '@/services/UserService'
import providerTypeEnum from '@/utils/providerTypeEnum'
import account from "./components/accountCenter/account.vue"
import basicInfor from "./components/accountCenter/basicInfor.vue"
import passengerList from "./components/accountCenter/passengerList.vue"
const { t } = useI18n();
const router = useRouter()
const { params } = router.currentRoute.value
const userStore = useUserStore()
const systemConfigStore = useSystemConfigStore()
const systemConfig = reactive({
tenantId: systemConfigStore.tenantId || null,
distributorId: systemConfigStore.distributorId as any,
})
const loginType = ref(userStore.loginType)
const userInfor = ref<any>(userStore.personalInfor)
const loading = ref(true)
// 绑定Google账号
const googleButtonContainer = ref(null);
// 绑定账号信息
const WeChatInfor = ref(null as any)
const GoogleInfor = ref(null as any)
const LnlineInfor = ref(null as any)
// 打开微信绑定账号弹窗信息
const openInfo = ref({} as any)
const current = ref(1)
if(params&&params.type){
......
......@@ -246,7 +246,6 @@ const getAppIdRedirectUri = async () => {
// 获取微信绑定AppID 域名
const response = await userStore.getUserWechatAppIdAsync(systemConfig.tenantId?.toString() || '',providerTypeEnum.WECHAT.value)
if (response.status == 'SUCCESS') {
return
openInfo.value = response.data[0]
const { appId, redirectUri } = openInfo.value;
const redirect_url = redirectUri?redirectUri:'https://www.oytour.com/#/login'
......
......@@ -79,12 +79,26 @@
<a-select class="formData-input !w-[289px] mr-[30px]"
v-model="formData.residentialArea"
size="large"
:placeholder="t('personal.placeholder')">
<a-option v-for="item of AreaCodeList" :value="item.phoneCode" :label="item.name" />
:placeholder="t('personal.placeholder')"
allow-search>
<a-option v-for="item of AreaCodeList" :value="item.id" :label="item.name" />
</a-select>
</a-form-item>
</a-col>
<a-col :span="8"></a-col>
<a-col :span="8">
<a-form-item field="phoneCode" :label="t('login.phoneCode')">
<a-select class="formData-input !w-[289px] mr-[30px]"
v-model="formData.phoneCode"
size="large"
allow-search>
<a-option v-for="item of AreaCodeList" :value="item.phoneCode" :label="item.phoneCode" >
<span>{{ item.name }}</span>
<span>{{ item.phoneCode }}</span>
</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item field="phone" :label="t('login.bindingPhone')">
<a-input class="formData-input !w-[289px] mr-[30px]"
......@@ -92,7 +106,7 @@
:placeholder="t('login.bindingPhoneRequired')"
size="large">
<template #prefix>
<a-dropdown position="br" trigger.stop="click">
<!-- <a-dropdown position="br" trigger.stop="click">
<div class="flex items-center cursor-pointer">
<span class="text-[16px] font-bold w-[45px]">{{ formData.phoneCode?formData.phoneCode:t('login.phoneCode') }}</span>
<icon-down size="16" strokeLinejoin="miter" />
......@@ -104,13 +118,12 @@
:key="option.value"
@click.stop="handleAreaCodeChange(option.phoneCode)">
<div class="flex items-center space-x-3 px-2 py-1">
<!-- <span class="text-lg">{{ option.flag }}</span> -->
<span class="font-medium">{{ option.name }}</span>
<span class="text-[#A3A4A0] font-light">({{ option.phoneCode }})</span>
</div>
</a-doption>
</template>
</a-dropdown>
</a-dropdown> -->
</template>
</a-input>
......@@ -157,8 +170,6 @@ import CountryService from '@/services/CountryService'
import { useUserStore } from '@/stores/user'
import UploadService from '@/services/UploadService'
import type { FileItem } from '@arco-design/web-vue'
import { errorMonitor } from "events";
const { t } = useI18n();
......@@ -187,12 +198,10 @@ const formData = reactive({
name: '',//名
surName: '',//姓氏
birthday: null as any,//出生日期
isReceivePush: null as any,//勾選「我願意接收優惠與電子報」 1是
lineId: null as any,//LINE ID
phone: null as any,//手机号
phoneCode: null as any,//手机号国家码
photo: null as any,//头像
residentialArea: null as any,//居住地
residentialArea: null as any,//国籍
sex: null as any,//性别 1男 2女
wechatId: null as any,//微信ID
})
......@@ -318,7 +327,10 @@ const initPullDown = () =>{
const getSimples = async () => {
const result = await CountryService.getSimpleList(true)
if(result&&result.length>0){
AreaCodeList.value = result
AreaCodeList.value = result.map((item: any) => ({
...item,
phoneCode: item.phoneCode.indexOf('+')==-1?'+'+item.phoneCode:item.phoneCode,
}))
}
}
......
<template>
<a-spin :loading="loading" class="w-full">
<div v-if="!showType">
<div>
<div class="accountCenter rounded-[14px]
border-[1px] mb-[28px] flex items-center
justify-between py-[30px] pl-[43px] pr-[35px]"
v-for="(item,index) in dataList">
<div class="w-[100px]">
{{ item.name }}
</div>
<div class="w-[100px]">
{{ item.surName }}
</div>
<div class="w-[50px]">
{{ item.sex === 1 ? t('ORDER.GENDER_MALE') : t('ORDER.GENDER_FEMALE') }}
</div>
<div class="w-[200px]">
{{ item.email }}
</div>
<div class="w-[150px]">
{{item.phoneCode}}{{ item.phone }}
</div>
<div class="flex items-center justify-between">
<div class="w-[16px] h-[16px] editIcon" @click="clickItem(item,2)">&nbsp;</div>
<div class="w-[14px] h-[16px] deleteIcon ml-[17px]" @click="clickItem(item)">&nbsp;</div>
</div>
</div>
</div>
<div class="rounded-[8px] addGuestButton py-[10px] text-center text-base cursor-pointer" @click="showType = 1">{{ t('personal.addPassenger') }}</div>
</div>
<div v-if="showType" class="addPassenger h-[581px] bg-[#FFFFFF] rounded-b-[12px] shadow-lg shadow-[#E3E6DA]">
<div class="addPassengerHeader py-[20px] px-[31px] rounded-t-[12px] text-base">{{ showType === 1 ? t('personal.addPassenger') : t('personal.editPassenger') }}</div>
<a-space direction="vertical" class="w-full p-[40px]">
<!-- horizonta -->
<a-form :model="formData" :rules="rules" layout="vertical" class="w-full">
<a-row class="w-full">
<a-col :span="8">
<a-form-item :label="t('personal.firstName')" field="name" required>
<a-input class="formData-input !w-[289px] mr-[30px]"
v-model="formData.name"
size="large">
</a-input>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item :label="t('personal.lastName')" field="surName" required>
<a-input class="formData-input !w-[289px] mr-[30px]"
v-model="formData.surName"
size="large">
<template #suffix>
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item field="sex" :label="t('personal.gender')">
<a-select class="formData-input !w-[289px] mr-[30px]"
v-model="formData.sex"
size="large"
:placeholder="t('personal.gender')">
<a-option v-for="item of genderOptions" :value="item.value" :label="item.label" />
</a-select>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item field="birthday" :label="t('personal.birthday')">
<a-date-picker class="formData-input !w-[289px] w-full mr-[30px]"
v-model="formData.birthday"
size="large"
placeholder="YYYY/MM/DD"
type="date"
format="YYYY/MM/DD"
value-format="YYYY-MM-DD"
>
</a-date-picker>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item field="countryId" :label="t('CustomField.Participant.Nationality')" required>
<a-select class="formData-input !w-[289px] mr-[30px]"
v-model="formData.countryId"
size="large"
:placeholder="t('personal.placeholder')"
allow-search>
<a-option v-for="item of AreaCodeList" :value="item.id" :label="item.name" />
</a-select>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item :label="t('personal.idCard')" field="surName" required>
<a-input class="formData-input !w-[289px] mr-[30px]"
v-model="formData.idCard"
size="large">
<template #suffix>
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item field="phoneCode" :label="t('login.phoneCode')">
<a-select class="formData-input !w-[289px] mr-[30px]"
v-model="formData.phoneCode"
size="large"
allow-search>
<a-option v-for="item of AreaCodeList" :value="item.phoneCode" :label="item.phoneCode" >
<span>{{ item.name }}</span>
<span>{{ item.phoneCode }}</span>
</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item field="phone" :label="t('login.bindingPhone')">
<a-input class="formData-input !w-[289px] mr-[30px]"
v-model="formData.phone"
:placeholder="t('login.bindingPhoneRequired')"
size="large"></a-input>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item :label="t('login.emailLogin')" field="">
<a-input class="formData-input !w-[289px] mr-[30px]"
v-model="formData.email"
size="large">
<template #suffix>
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item field="" label="" class="mt-[95px] flex">
<div class="flex-1"></div>
<a-button
size="large"
:loading="loading"
html-type="submit"
class="!w-[159px] !bg-[#FFFFFF] !border-[1px] !border-[#E3E6DA] SourceHanSansCN font-bold text-gray-200 !h-[46px] !rounded-[13px] !text-base mr-[37px]"
@click="cancel()"
>
{{ t('personal.cancel') }}
</a-button>
<a-button
type="primary"
size="large"
:loading="loading"
html-type="submit"
class="SubmitBox !w-[159px] SourceHanSansCN font-bold text-gray-200 !h-[46px] !rounded-[13px] !text-base mr-[37px]"
:class="[loading?'active':'']"
@click="handleSubmit()"
>
{{ t('personal.save') }}
</a-button>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-space>
</div>
</a-spin>
</template>
<script setup lang="ts">
import { ref,reactive, onMounted, watch, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useUserStore } from '@/stores/user'
import { useSystemConfigStore } from '@/stores/index'
import { Message } from '@arco-design/web-vue'
import { query } from '@/utils/common'
import { useRouter } from 'vue-router'
import UserService from '@/services/UserService'
import providerTypeEnum from '@/utils/providerTypeEnum'
import CountryService from '@/services/CountryService'
const { t } = useI18n();
const router = useRouter()
const userStore = useUserStore()
const systemConfigStore = useSystemConfigStore()
const systemConfig = reactive({
tenantId: systemConfigStore.tenantId || null,
distributorId: systemConfigStore.distributorId as any,
})
const loginType = ref(userStore.loginType)
const userInfor = ref<any>(userStore.personalInfor)
const loading = ref(true)
// 绑定Google账号
const googleButtonContainer = ref(null);
// 绑定账号信息
const WeChatInfor = ref(null as any)
const GoogleInfor = ref(null as any)
const LnlineInfor = ref(null as any)
// 打开微信绑定账号弹窗信息
const openInfo = ref({} as any)
const genderOptions = ref([
{
label: t('ORDER.GENDER_MALE'),
value: 'MALE'
},
{
label: t('ORDER.GENDER_FEMALE'),
value: 'FEMALE'
},
])
const formData = reactive({
id: 0,//旅客ID
name: '',//名
surName: '',//姓氏
birthday: null as any,//出生日期
phone: null as any,//手机号
phoneCode: null as any,//手机号国家码
photo: null as any,//头像
countryId: null as any,//国籍
sex: null as any,//性别 1男 2女
idCard: null as any,//身份证号
email: null as any,//邮箱
})
// 验证规则调整
const rules = computed(() => ({
name: [
{ required: true, message: t('personal.placeholderFirstName') },
],
surName: [
{ required: true, message: t('personal.placeholderLastName') },
],
gender: [
{
required: true,
message: t('personal.placeholderGender'),
},
],
}))
const AreaCodeList = ref([] as any[])
const params = reactive({
SkipCount: 0,//跳过的记录数
MaxResultCount: 1,//最大结果数
Name: '',
})
const showType = ref('' as null)
const dataList = ref([])
const initPullDown = () =>{
loadData()
getSimples()
}
const getSimples = async () => {
const result = await CountryService.getSimpleList(true)
if(result&&result.length>0){
AreaCodeList.value = result.map((item: any) => ({
...item,
phoneCode: item.phoneCode.indexOf('+')==-1?'+'+item.phoneCode:item.phoneCode,
}))
}
}
const loadData = async () => {
loading.value = true
try{
const response = await UserService.memberGuest(systemConfig.tenantId,params)
if(response){
dataList.value = response.items || []
loading.value = false
}
} catch (error: any) {
console.error('加载失败:', error)
Message.error(error.message)
} finally {
loading.value = false
}
}
const clickItem = (item: any,type: number = 1) => {
if(type==1){
showType.value = 2
formData.id = item.id || ''
formData.name = item.name || ''
formData.surName = item.surName || ''
formData.birthday = item.birthday || null
formData.isReceivePush = item.isReceivePush || null
formData.lineId = item.lineId || null
formData.phone = item.phone || null
formData.phoneCode = item.phoneCode || null
formData.photo = item.photo || null
}else{
}
}
const deleteItem = async (item: any) => {
if(!item.id){
Message.warning(t('personal.placeholderDelete'))
return
}
loading.value = true
try {
const response = await UserService.deleteMemberGuest(systemConfig.tenantId || 'default',item.id)
if (response) {
Message.success(t('personal.deleteSuccess'))
loadData()
}
} catch (error: any) {
console.error('删除失败:', error)
Message.error(error.message)
} finally {
loading.value = false
}
}
const cancel = () => {
showType.value = null
formData.id = ''
formData.name = ''
formData.surName = ''
formData.birthday = null
formData.isReceivePush = null
formData.lineId = null
formData.phone = null
formData.phoneCode = null
formData.photo = null
formData.countryId = null
formData.sex = null
formData.wechatId = null
}
const verification = () => {
let msg = ''
// 简单验证必填字段
if (msg==''&&(formData.name==''||!formData.name
||userInfor.value.surName==''||!formData.surName
|| formData.countryId==''||!formData.countryId
|| formData.idCard==''||!formData.idCard
)) {
msg = t('login.pleaseComplete')
}
return msg
}
const handleSubmit = async () => {
const msg = verification()
if(msg){
Message.warning(msg)
return
}
loading.value = true
try {
const registerData = formData
const response = await UserService.updateMemberGuest(formData.tenantId || 'default',registerData)
console.log(response,'========------')
if (response) {
loadData()
}
} catch (error: any) {
console.error('提交失败:', error)
Message.error(error.message)
} finally {
loading.value = false
}
}
onMounted(async () => {
try {
} catch (error) {
console.error('SDK 初始化失败:', error);
}
});
initPullDown()
</script>
<style scoped lang="scss">
:deep(.arco-form-item-content){
min-height: 46px;
border-radius: 12px;
}
:deep(.arco-input-wrapper){
height: 46px;
border-radius: 12px !important;
background-color: #FFFFFF;
border: 1px solid rgb(var(--gray-6));
}
:deep(.arco-input-focus){
border-radius: 12px;
box-shadow: rgba(60,85,62,0.18) 0px 5px 15px;
border: 2px solid rgb(var(--arcoblue-6));
background-color: #FFFFFF;
}
:deep(.arco-select-view-single){
height: 46px;
border-radius: 12px !important;
background-color: #FFFFFF;
border: 1px solid rgb(var(--gray-6));
}
:deep(.arco-select-view-single.arco-select-view-focus){
border-radius: 12px;
box-shadow: rgba(60,85,62,0.18) 0px 5px 15px;
border: 2px solid rgb(var(--arcoblue-6));
background-color: #FFFFFF;
}
:deep(.arco-picker){
height: 46px;
border-radius: 12px !important;
background-color: #FFFFFF;
border: 1px solid rgb(var(--gray-6));
}
:deep(.arco-picker.arco-picker-focused){
border-radius: 12px;
box-shadow: rgba(60,85,62,0.18) 0px 5px 15px;
border: 2px solid rgb(var(--arcoblue-6));
background-color: #FFFFFF;
}
.editIcon{
width: 16px;
height: 16px;
background: url('../../../../assets/images/personal/pen_2.png')no-repeat;
background-size: 100% 100%;
}
.editIcon:hover{
background: url('../../../../assets/images/personal/pen.png')no-repeat;
}
.deleteIcon{
width: 16px;
height: 16px;
background: url('../../../../assets/images/personal/sc_2.png')no-repeat;
background-size: 100% 100%;
}
.deleteIcon:hover{
background: url('../../../../assets/images/personal/sc_1.png')no-repeat;
}
.addPassenger{
background: #FFFFFF;
// box-shadow: 2px 6px 29px 3px #E3E6DA;
}
.addPassengerHeader{
background-color: var(--customPrimary-7);
}
.addGuestButton{
background-color: var(--customPrimary-7);
}
:deep(.phoneCodeBox .arco-input-wrapper){
width: 85px;
height: 40px;
border: 0;
}
:deep(.phoneCodeBox .arco-input-focus){
width: 45px;
height: 40px;
border: 0;
}
</style>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment