员工端h5版本代码提交
This commit is contained in:
56
src/api/auth.ts
Normal file
56
src/api/auth.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import http from './request'
|
||||
import { mockApi } from './mock'
|
||||
import { env } from '@/utils/env'
|
||||
import type { TokenResponse } from '@/types/api'
|
||||
import { joinUrl } from '@/utils/url'
|
||||
|
||||
export interface OrganizationMemberInfo {
|
||||
memberId?: string
|
||||
organizationId?: string
|
||||
memberName?: string
|
||||
userPhone?: string
|
||||
memberRoleId?: string
|
||||
}
|
||||
|
||||
const basicAuth = () => {
|
||||
const raw = `${env.clientId}:${env.clientSecret}`
|
||||
return `Basic ${window.btoa(raw)}`
|
||||
}
|
||||
|
||||
export const sendMobileCode = async (phone: string) => {
|
||||
if (env.useMock) return mockApi.sendMobileCode(phone)
|
||||
return await http.get<boolean>(joinUrl(env.adminBase, 'platformUser/sendMobileCode', phone), {
|
||||
headers: {
|
||||
clientId: env.clientId,
|
||||
clientConfigId: env.clientConfigId
|
||||
}
|
||||
}) as unknown as boolean
|
||||
}
|
||||
|
||||
export const loginByMobile = async (phone: string, code: string): Promise<TokenResponse> => {
|
||||
if (env.useMock) return mockApi.loginByMobile(phone)
|
||||
|
||||
const form = new URLSearchParams()
|
||||
form.set('grant_type', 'mobile')
|
||||
form.set('mobile', phone)
|
||||
form.set('code', code)
|
||||
form.set('scope', 'server')
|
||||
if (env.clientConfigId) {
|
||||
form.set('clientConfigId', env.clientConfigId)
|
||||
}
|
||||
|
||||
return await http.post<TokenResponse>(joinUrl(env.authBase, 'oauth2/token'), form, {
|
||||
headers: {
|
||||
Authorization: basicAuth(),
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
}) as unknown as TokenResponse
|
||||
}
|
||||
|
||||
export const bindUserInfoAndGetUserMemberInfoByPhone = async (phone: string) => {
|
||||
if (env.useMock) return mockApi.bindOrganization(phone)
|
||||
return await http.post<OrganizationMemberInfo>(
|
||||
joinUrl(env.staffBase, 'organizationMember/bindUserInfoAndGetUserMemberInfoByPhone'),
|
||||
{ userPhone: phone }
|
||||
) as unknown as OrganizationMemberInfo
|
||||
}
|
||||
19
src/api/events.ts
Normal file
19
src/api/events.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import http from './request'
|
||||
import { mockApi } from './mock'
|
||||
import { env } from '@/utils/env'
|
||||
import type { PageResponse } from '@/types/api'
|
||||
import type { CreateEventPayload, EventData, EventSearch } from '@/types/event'
|
||||
import { joinUrl } from '@/utils/url'
|
||||
|
||||
export const fetchEventList = async (query: EventSearch): Promise<PageResponse<EventData>> => {
|
||||
if (env.useMock) return mockApi.eventList(query)
|
||||
return await http.post<PageResponse<EventData>>(
|
||||
joinUrl(env.staffBase, 'event/eventList'),
|
||||
query
|
||||
) as unknown as PageResponse<EventData>
|
||||
}
|
||||
|
||||
export const createEvent = async (payload: CreateEventPayload) => {
|
||||
if (env.useMock) return mockApi.createEvent(payload)
|
||||
return http.post<boolean>(joinUrl(env.staffBase, 'event/createEvent'), payload)
|
||||
}
|
||||
295
src/api/mock.ts
Normal file
295
src/api/mock.ts
Normal file
@@ -0,0 +1,295 @@
|
||||
import dayjs from 'dayjs'
|
||||
import type { PageResponse, TokenResponse } from '@/types/api'
|
||||
import type {
|
||||
CommodityOrder,
|
||||
CommodityOrderSearch,
|
||||
UserOrderDetail,
|
||||
WriteOffPayload,
|
||||
WriteOffResult
|
||||
} from '@/types/order'
|
||||
import type { CreateEventPayload, EventData, EventSearch } from '@/types/event'
|
||||
import { addDays, formatDateTime, today } from '@/utils/date'
|
||||
|
||||
const wait = (ms = 260) => new Promise((resolve) => window.setTimeout(resolve, ms))
|
||||
|
||||
const coverImages = [
|
||||
'https://images.unsplash.com/photo-1566073771259-6a8506099945?auto=format&fit=crop&w=900&q=80',
|
||||
'https://images.unsplash.com/photo-1542314831-068cd1dbfeeb?auto=format&fit=crop&w=900&q=80',
|
||||
'https://images.unsplash.com/photo-1551882547-ff40c63fe5fa?auto=format&fit=crop&w=900&q=80'
|
||||
]
|
||||
|
||||
let orders: CommodityOrder[] = [
|
||||
{
|
||||
id: '202606160001',
|
||||
orderAmt: '699.00',
|
||||
visitorName: '林清',
|
||||
contactPhone: '13800000001',
|
||||
checkInData: today(),
|
||||
checkOutData: addDays(1),
|
||||
orderStatus: '2',
|
||||
payStatus: '1',
|
||||
payAmt: '659.00',
|
||||
discountAmt: '40.00',
|
||||
payTime: formatDateTime(dayjs().subtract(2, 'hour').toDate()),
|
||||
orderType: '0',
|
||||
createTime: formatDateTime(dayjs().subtract(3, 'hour').toDate()),
|
||||
roomId: '1208',
|
||||
commodityName: '湖景大床房含双早',
|
||||
commodityCoverPhoto: coverImages[0],
|
||||
commodityAmount: 1,
|
||||
reservationDate: today()
|
||||
},
|
||||
{
|
||||
id: '202606150108',
|
||||
orderAmt: '238.00',
|
||||
visitorName: '王予安',
|
||||
contactPhone: '13800000002',
|
||||
orderStatus: '6',
|
||||
payStatus: '1',
|
||||
payAmt: '238.00',
|
||||
orderType: '2',
|
||||
createTime: formatDateTime(dayjs().subtract(1, 'day').toDate()),
|
||||
commodityName: '云端餐厅双人套餐',
|
||||
commodityCoverPhoto: coverImages[1],
|
||||
commodityAmount: 1,
|
||||
writeOffTime: formatDateTime(dayjs().subtract(4, 'hour').toDate())
|
||||
},
|
||||
{
|
||||
id: '202606140072',
|
||||
orderAmt: '158.00',
|
||||
visitorName: '赵一鸣',
|
||||
contactPhone: '13800000003',
|
||||
orderStatus: '4',
|
||||
payStatus: '1',
|
||||
payAmt: '158.00',
|
||||
orderType: '1',
|
||||
createTime: formatDateTime(dayjs().subtract(2, 'day').toDate()),
|
||||
commodityName: '温泉中心成人票',
|
||||
commodityCoverPhoto: coverImages[2],
|
||||
commodityAmount: 2
|
||||
},
|
||||
{
|
||||
id: '202606130045',
|
||||
orderAmt: '899.00',
|
||||
visitorName: '许知夏',
|
||||
contactPhone: '13800000004',
|
||||
checkInData: addDays(2),
|
||||
checkOutData: addDays(3),
|
||||
orderStatus: '1',
|
||||
payStatus: '1',
|
||||
payAmt: '899.00',
|
||||
orderType: '0',
|
||||
createTime: formatDateTime(dayjs().subtract(3, 'day').toDate()),
|
||||
commodityName: '亲子套房一晚',
|
||||
commodityCoverPhoto: coverImages[0],
|
||||
commodityAmount: 1,
|
||||
reservationDate: addDays(2)
|
||||
}
|
||||
]
|
||||
|
||||
let events: EventData[] = [
|
||||
{
|
||||
id: 'EVT20260616001',
|
||||
entityName: '前台',
|
||||
eventDescription: '端午假期入住高峰,前台增开一组接待人员并优先处理已预付订单。',
|
||||
eventStatus: 0,
|
||||
effectiveStartTime: formatDateTime(dayjs().subtract(1, 'hour').toDate()),
|
||||
effectiveEndTime: formatDateTime(dayjs().add(8, 'hour').toDate()),
|
||||
createByName: '值班经理',
|
||||
popUpReminder: 1,
|
||||
publicTime: formatDateTime(dayjs().subtract(2, 'hour').toDate()),
|
||||
imgUrl: coverImages[0],
|
||||
showStatusDesc: '生效中'
|
||||
},
|
||||
{
|
||||
id: 'EVT20260615002',
|
||||
entityName: '餐饮部',
|
||||
eventDescription: '晚餐第二时段预约已满,建议推荐客人选择 19:45 后到店。',
|
||||
eventStatus: 0,
|
||||
effectiveStartTime: formatDateTime(dayjs().add(1, 'hour').toDate()),
|
||||
effectiveEndTime: formatDateTime(dayjs().add(10, 'hour').toDate()),
|
||||
createByName: '餐饮主管',
|
||||
popUpReminder: 0,
|
||||
publicTime: formatDateTime(dayjs().subtract(1, 'day').toDate()),
|
||||
imgUrl: coverImages[1],
|
||||
showStatusDesc: '待生效'
|
||||
}
|
||||
]
|
||||
|
||||
const page = <T>(records: T[], pageNum: number, pageSize: number): PageResponse<T> => {
|
||||
const start = (pageNum - 1) * pageSize
|
||||
const current = records.slice(start, start + pageSize)
|
||||
return {
|
||||
records: current,
|
||||
total: records.length,
|
||||
size: pageSize,
|
||||
current: pageNum,
|
||||
pages: Math.ceil(records.length / pageSize)
|
||||
}
|
||||
}
|
||||
|
||||
export const mockApi = {
|
||||
async sendMobileCode(phone: string) {
|
||||
await wait()
|
||||
return { data: true, msg: phone ? '5678nianxx' : '请输入手机号' }
|
||||
},
|
||||
|
||||
async loginByMobile(phone: string): Promise<TokenResponse> {
|
||||
await wait()
|
||||
return {
|
||||
access_token: `mock-token-${Date.now()}`,
|
||||
refresh_token: 'mock-refresh-token',
|
||||
token_type: 'Bearer',
|
||||
expires_in: 43200,
|
||||
scope: 'server',
|
||||
client_id: 'custom',
|
||||
clientId: 'custom',
|
||||
user_id: '10001',
|
||||
username: phone,
|
||||
tenant_id: 1
|
||||
}
|
||||
},
|
||||
|
||||
async checkBindPhone() {
|
||||
await wait(120)
|
||||
return true
|
||||
},
|
||||
|
||||
async bindOrganization(phone: string) {
|
||||
await wait(180)
|
||||
if (!phone) {
|
||||
throw new Error('未绑定组织,请联系管理员')
|
||||
}
|
||||
return {
|
||||
memberId: 'MEMBER10001',
|
||||
organizationId: 'ORG10001',
|
||||
memberName: '当前员工',
|
||||
userPhone: phone,
|
||||
memberRoleId: 'ROLE_STAFF'
|
||||
}
|
||||
},
|
||||
|
||||
async orderList(query: CommodityOrderSearch) {
|
||||
await wait()
|
||||
const keyword = query.orderId || query.contactPhone || ''
|
||||
const filtered = orders.filter((item) => {
|
||||
const matchStatus = query.orderStatus ? item.orderStatus === query.orderStatus : true
|
||||
const matchType = query.commodityTypeCode ? item.orderType === query.commodityTypeCode : true
|
||||
const matchKeyword = keyword
|
||||
? item.id.includes(keyword) || item.contactPhone.includes(keyword) || item.visitorName.includes(keyword)
|
||||
: true
|
||||
return matchStatus && matchType && matchKeyword
|
||||
})
|
||||
return page(filtered, query.pageNum, query.pageSize)
|
||||
},
|
||||
|
||||
async orderDetail(orderId: string): Promise<UserOrderDetail> {
|
||||
await wait()
|
||||
const order = orders.find((item) => item.id === orderId) || orders[0]
|
||||
return {
|
||||
orderId: order.id,
|
||||
orderAmt: order.orderAmt,
|
||||
consumerInfoList: [
|
||||
{
|
||||
id: '1',
|
||||
visitorName: order.visitorName,
|
||||
contactPhone: order.contactPhone,
|
||||
idCard: '430***********2218'
|
||||
}
|
||||
],
|
||||
checkInData: order.checkInData,
|
||||
checkOutData: order.checkOutData,
|
||||
reservationDate: order.reservationDate,
|
||||
orderStatus: order.orderStatus,
|
||||
userId: order.userId,
|
||||
organizationId: order.organizationId,
|
||||
payStatus: order.payStatus,
|
||||
payAmt: order.payAmt,
|
||||
discountAmt: order.discountAmt,
|
||||
payTime: order.payTime,
|
||||
createTime: order.createTime,
|
||||
commodityCoverPhoto: order.commodityCoverPhoto,
|
||||
commodityName: order.commodityName,
|
||||
commodityAddress: '星河湾酒店一层前台',
|
||||
commodityServiceList: [{ serviceName: '到店核销' }, { serviceName: '不可拆分使用' }],
|
||||
commodityTip: '请在预约日期到店使用,核销后不支持退款。',
|
||||
payWay: '0',
|
||||
paySerialNumber: `WX${order.id}`,
|
||||
paySource: '2',
|
||||
orderType: order.orderType,
|
||||
commodityAmount: String(order.commodityAmount),
|
||||
storeName: '星河湾酒店',
|
||||
complaintHotline: '18285123187',
|
||||
refundable: order.orderStatus !== '6',
|
||||
commodityId: `COM${order.id.slice(-4)}`,
|
||||
commodityTypeCode: order.orderType,
|
||||
commodityFacilityList: ['含早餐', '免费停车', '可预约'],
|
||||
commodityDescription: '面向到店客人的酒店权益商品,需由员工确认后核销。',
|
||||
commodityPackageConfig: [
|
||||
{
|
||||
packageName: order.commodityName,
|
||||
packageContent: `${order.commodityName} x ${order.commodityAmount}`
|
||||
}
|
||||
],
|
||||
reservationEnabled: order.reservationDate ? 1 : 0,
|
||||
reservationRuleDesc: '预约订单请核对预约日期和联系人信息。',
|
||||
writeOffRecordList: order.writeOffTime
|
||||
? [
|
||||
{
|
||||
id: `WO${order.id}`,
|
||||
orderId: order.id,
|
||||
packageName: order.commodityName,
|
||||
userName: '当前员工',
|
||||
createTime: order.writeOffTime
|
||||
}
|
||||
]
|
||||
: []
|
||||
}
|
||||
},
|
||||
|
||||
async writeOff(payload: WriteOffPayload): Promise<WriteOffResult> {
|
||||
await wait()
|
||||
orders = orders.map((item) =>
|
||||
item.id === payload.orderId
|
||||
? { ...item, orderStatus: '6', writeOffTime: formatDateTime(new Date()) }
|
||||
: item
|
||||
)
|
||||
return {
|
||||
success: true,
|
||||
orderId: payload.orderId,
|
||||
packageName: payload.packageName,
|
||||
message: '核销成功',
|
||||
operatedAt: formatDateTime(new Date())
|
||||
}
|
||||
},
|
||||
|
||||
async eventList(query: EventSearch) {
|
||||
await wait()
|
||||
const filtered = events.filter((item) => {
|
||||
const matchName = query.entityName ? item.entityName.includes(query.entityName) : true
|
||||
const matchStatus = query.eventStatus === undefined ? true : item.eventStatus === query.eventStatus
|
||||
return matchName && matchStatus
|
||||
})
|
||||
return page(filtered, query.pageNum, query.pageSize)
|
||||
},
|
||||
|
||||
async createEvent(payload: CreateEventPayload) {
|
||||
await wait()
|
||||
events = [
|
||||
{
|
||||
id: `EVT${Date.now()}`,
|
||||
...payload,
|
||||
eventStatus: 0,
|
||||
createByName: '当前员工',
|
||||
showStatusDesc: '待生效'
|
||||
},
|
||||
...events
|
||||
]
|
||||
return true
|
||||
},
|
||||
|
||||
async uploadFile(file: File) {
|
||||
await wait()
|
||||
return URL.createObjectURL(file)
|
||||
}
|
||||
}
|
||||
40
src/api/orders.ts
Normal file
40
src/api/orders.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import http from './request'
|
||||
import { mockApi } from './mock'
|
||||
import { env } from '@/utils/env'
|
||||
import type { PageResponse } from '@/types/api'
|
||||
import { joinUrl } from '@/utils/url'
|
||||
import type {
|
||||
CommodityOrder,
|
||||
CommodityOrderSearch,
|
||||
UserOrderDetail,
|
||||
WriteOffPayload,
|
||||
WriteOffResult
|
||||
} from '@/types/order'
|
||||
|
||||
export const fetchOrderList = async (query: CommodityOrderSearch): Promise<PageResponse<CommodityOrder>> => {
|
||||
if (env.useMock) return mockApi.orderList(query)
|
||||
return await http.post<PageResponse<CommodityOrder>>(
|
||||
joinUrl(env.staffBase, 'order/commodityOrderList'),
|
||||
query
|
||||
) as unknown as PageResponse<CommodityOrder>
|
||||
}
|
||||
|
||||
export const fetchOrderDetail = async (orderId: string): Promise<UserOrderDetail> => {
|
||||
if (env.useMock) return mockApi.orderDetail(orderId)
|
||||
return await http.post<UserOrderDetail>(
|
||||
joinUrl(env.staffBase, 'order/userOrderDetail'),
|
||||
{ orderId }
|
||||
) as unknown as UserOrderDetail
|
||||
}
|
||||
|
||||
export const writeOffOrder = async (payload: WriteOffPayload): Promise<WriteOffResult> => {
|
||||
if (env.useMock) return mockApi.writeOff(payload)
|
||||
await http.post<boolean>(joinUrl(env.staffBase, 'order/writeOff'), payload)
|
||||
return {
|
||||
success: true,
|
||||
orderId: payload.orderId,
|
||||
packageName: payload.packageName,
|
||||
message: '核销成功',
|
||||
operatedAt: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
36
src/api/request.ts
Normal file
36
src/api/request.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import axios, { AxiosError } from 'axios'
|
||||
import { showToast } from 'vant'
|
||||
import { env } from '@/utils/env'
|
||||
|
||||
const http = axios.create({
|
||||
baseURL: env.apiBase,
|
||||
timeout: 15000
|
||||
})
|
||||
|
||||
http.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem('hotel_h5_access_token')
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
return config
|
||||
})
|
||||
|
||||
http.interceptors.response.use(
|
||||
(response) => {
|
||||
const payload = response.data
|
||||
if (payload && typeof payload === 'object' && 'code' in payload) {
|
||||
if (payload.code === 0 || payload.code === 200) {
|
||||
return payload.data === undefined ? payload : payload.data
|
||||
}
|
||||
return Promise.reject(new Error(payload.msg || '请求失败'))
|
||||
}
|
||||
return payload
|
||||
},
|
||||
(error: AxiosError<{ msg?: string }>) => {
|
||||
const message = error.response?.data?.msg || error.message || '网络异常'
|
||||
showToast(message)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default http
|
||||
20
src/api/upload.ts
Normal file
20
src/api/upload.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import http from './request'
|
||||
import { mockApi } from './mock'
|
||||
import { env } from '@/utils/env'
|
||||
import { joinUrl } from '@/utils/url'
|
||||
|
||||
interface UploadResult {
|
||||
url?: string
|
||||
}
|
||||
|
||||
export const uploadFile = async (file: File) => {
|
||||
if (env.useMock) return mockApi.uploadFile(file)
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
const result = await http.post<string | UploadResult>(joinUrl(env.hotelBizBase, 'hotBizCommon/upload'), form, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
}) as unknown as string | UploadResult
|
||||
return typeof result === 'string' ? result : result.url || ''
|
||||
}
|
||||
Reference in New Issue
Block a user