diff --git a/doc/价格管理平台.bmpr b/doc/价格管理平台.bmpr index 2e3bd02..eb1a8d6 100644 Binary files a/doc/价格管理平台.bmpr and b/doc/价格管理平台.bmpr differ diff --git a/package.json b/package.json index 7b26815..dd39be7 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,6 @@ "i18next": "^23.11.5", "i18next-browser-languagedetector": "^8.0.0", "i18next-http-backend": "^2.5.2", - "mobx": "^6.9.0", - "mobx-react": "^7.6.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-i18next": "^14.1.2", diff --git a/src/components/ErrorPage.jsx b/src/components/ErrorPage.jsx index 49b8f9d..9c779d0 100644 --- a/src/components/ErrorPage.jsx +++ b/src/components/ErrorPage.jsx @@ -1,12 +1,12 @@ -import { useRouteError } from "react-router-dom" +import { useRouteError } from 'react-router-dom' import { Result } from 'antd' export default function ErrorPage() { const errorResponse = useRouteError() return ( ) diff --git a/src/components/RequireAuth.jsx b/src/components/RequireAuth.jsx new file mode 100644 index 0000000..cabef0b --- /dev/null +++ b/src/components/RequireAuth.jsx @@ -0,0 +1,22 @@ +import { Result } from 'antd' +import { usingStorage } from '@/hooks/usingStorage' +import useAuthStore from '@/stores/Auth' + +export default function RequireAuth({ children, ...props }) { + + const isPermitted = useAuthStore((state) => state.isPermitted) + const { userId } = usingStorage() + + if (isPermitted(props.subject)) { + // if (props.subject === '/account/management1') { + return children + } else if (props.result) { + return ( + + ) + } +} \ No newline at end of file diff --git a/src/components/SearchForm.jsx b/src/components/SearchForm.jsx index a669294..979a6fc 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -208,6 +208,22 @@ function getFields(props) { , fieldProps?.dates?.col || midCol ), + item( + 'username', + 3, + + + , + fieldProps?.username?.col || 4 + ), + item( + 'realname', + 4, + + + , + fieldProps?.realname?.col || 4 + ), /** * */ diff --git a/src/config.js b/src/config.js index 58e13a2..fc69f82 100644 --- a/src/config.js +++ b/src/config.js @@ -6,6 +6,28 @@ export const HT_HOST = import.meta.env.PROD ? "https://p9axztuwd7x8a7.mycht.cn" export const DATE_FORMAT = "YYYY-MM-DD"; export const DATE_FORMAT_MONTH = "YYYY-MM"; export const SMALL_DATETIME_FORMAT = "YYYY-MM-DD 23:59"; +export const OFFICEWEBVIEWERURL = "https://view.officeapps.live.com/op/embed.aspx?wdPrint=1&wdHideGridlines=0&wdHideComments=1&wdEmbedCode=0&src="; const __BUILD_VERSION__ = `__BUILD_VERSION__`.replace(/"/g, '') export const BUILD_VERSION = import.meta.env.PROD ? __BUILD_VERSION__ : import.meta.env.MODE; + +// 权限常量定义 +// 账号、权限管理 +// category: system +export const PERM_ACCOUNT_MANAGEMENT = '/account/management' +export const PERM_ACCOUNT_NEW = '/account/new' +export const PERM_ACCOUNT_DISABLE = '/account/disable' +export const PERM_ACCOUNT_RESET_PASSWORD = '/account/reset-password' +export const PERM_ROLE_NEW = '/account/role/new' + +// 海外供应商 +// category: oversea +export const PERM_OVERSEA = '/oversea/all' + +// 国内供应商 +// category: domestic +export const PERM_DOMESTIC = '/domestic/all' + +// 机票供应商 +// category: air-ticket +export const PERM_AIR_TICKET = '/air-ticket/all' diff --git a/src/main.jsx b/src/main.jsx index dae7d6e..d78954e 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -5,8 +5,6 @@ import { createBrowserRouter, RouterProvider, } from "react-router-dom"; -import RootStore from "@/stores/Root"; -import { StoreContext } from '@/stores/StoreContext'; import "@/assets/global.css"; import App from "@/views/App"; import Standlone from "@/views/Standlone"; @@ -14,11 +12,13 @@ import Login from "@/views/Login"; import Logout from "@/views/Logout"; import Index from "@/views/index"; import ErrorPage from "@/components/ErrorPage"; +import RequireAuth from '@/components/RequireAuth' import ReservationNewest from "@/views/reservation/Newest"; import ReservationDetail from "@/views/reservation/Detail"; import ChangePassword from "@/views/account/ChangePassword"; import AccountProfile from "@/views/account/Profile"; import AccountManagement from "@/views/account/Management"; +import RoleList from "@/views/account/RoleList"; import FeedbackIndex from "@/views/feedback/Index"; import FeedbackDetail from "@/views/feedback/Detail"; import FeedbackCustomerDetail from "@/views/feedback/CustomerDetail"; @@ -30,10 +30,15 @@ import InvoiceDetail from "@/views/invoice/Detail"; import InvoicePaid from "@/views/invoice/Paid"; import InvoicePaidDetail from "@/views/invoice/PaidDetail"; import Airticket from "@/views/airticket/Index"; +import AirticketPlan from "@/views/airticket/Plan"; +import { ThemeContext } from '@/stores/ThemeContext' + import ProductsIndex from '@/views/products/Index'; import ProductsDetail from '@/views/products/Detail'; import ProductsAudit from '@/views/products/Audit'; import Gysgl from "@/views/gysgl/Index" +import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA, PERM_AIR_TICKET } from '@/config' + import './i18n'; configure({ @@ -52,22 +57,24 @@ const router = createBrowserRouter([ errorElement: , children: [ { index: true, element: }, - { path: "reservation/newest", element: }, - { path: "reservation/:reservationId", element: }, { path: "account/change-password", element: }, { path: "account/profile", element: }, - { path: "account/management", element: }, - { path: "feedback", element: }, - { path: "feedback/:GRI_SN/:CII_SN/:RefNo", element: }, - { path: "feedback/:GRI_SN/:RefNo", element: }, - { path: "report", element: }, - { path: "notice", element: }, - { path: "notice/:CCP_BLID", element: }, - { path: "invoice",element:}, - { path: "invoice/detail/:GMDSN/:GSN",element:}, - { path: "invoice/paid",element:}, - { path: "invoice/paid/detail/:flid",element:}, - { path: "airticket",element:}, + { path: "account/management", element: }, + { path: "account/role-list", element: }, + { path: "reservation/newest", element: }, + { path: "reservation/:reservationId", element: }, + { path: "feedback", element: }, + { path: "feedback/:GRI_SN/:CII_SN/:RefNo", element: }, + { path: "feedback/:GRI_SN/:RefNo", element: }, + { path: "report", element: }, + { path: "notice", element: }, + { path: "notice/:CCP_BLID", element: }, + { path: "invoice",element:}, + { path: "invoice/detail/:GMDSN/:GSN",element:}, + { path: "invoice/paid",element:}, + { path: "invoice/paid/detail/:flid", element: }, + { path: "airticket",element: }, + { path: "airticket/plan/:coli_sn",element:}, { path: "products",element:}, { path: "products/:travel_agency_id/:use_year/:audit_state/audit",element:}, { path: "products/:travel_agency_id/:use_year/:audit_state/edit",element:}, @@ -83,15 +90,14 @@ const router = createBrowserRouter([ } ]); -const rootStore = new RootStore(); ReactDOM.createRoot(document.getElementById("root")).render( // - -
Loading...
} - /> -
+ +
Loading...
} + /> +
//
); diff --git a/src/stores/Account.js b/src/stores/Account.js new file mode 100644 index 0000000..6c6410e --- /dev/null +++ b/src/stores/Account.js @@ -0,0 +1,141 @@ +import { create } from 'zustand' +import { fetchJSON, postForm } from '@/utils/request' +import { HT_HOST } from "@/config" +import { usingStorage } from '@/hooks/usingStorage' + +export const postAccountStatus = async (formData) => { + + const { errcode, result } = await postForm( + `${HT_HOST}/service-CooperateSOA/set_account_status`, formData) + return errcode !== 0 ? {} : result +} + +export const postAccountPassword = async (formData) => { + + const { errcode, result } = await postForm( + `${HT_HOST}/service-CooperateSOA/reset_account_password`, formData) + return errcode !== 0 ? {} : result +} + +export const fetchAccountList = async (params) => { + + const { errcode, result } = await fetchJSON( + `${HT_HOST}/service-CooperateSOA/search_account`, params) + return errcode !== 0 ? {} : result +} + +export const postAccountForm = async (formData) => { + + const { errcode, result } = await postForm( + `${HT_HOST}/service-CooperateSOA/new_or_update_account`, formData) + return errcode !== 0 ? {} : result +} + +export const postRoleForm = async (formData) => { + + const { errcode, result } = await postForm( + `${HT_HOST}/service-CooperateSOA/new_or_update_role`, formData) + return errcode !== 0 ? {} : result +} + +export const fetchRoleList = async () => { + + const { errcode, result } = await fetchJSON( + `${HT_HOST}/service-CooperateSOA/get_role_list`) + return errcode !== 0 ? {} : result +} + +const useAccountStore = create((set, get) => ({ + + accountList: [], + + disableAccount: async (accountId) => { + + const formData = new FormData() + formData.append('wu_id', accountId) + // enable disable + formData.append('account_status', 'disable') + + const result = await postAccountStatus(formData) + + console.info(result) + }, + + resetAccountPassword: async (accountId, password) => { + + const formData = new FormData() + formData.append('wu_id', accountId) + formData.append('newPassword', password) + + return postAccountPassword(formData) + }, + + newRole: () => { + return { + role_id: null, + role_name: '', + role_ids: '' + } + }, + + saveOrUpdateRole: async (formValues) => { + const formData = new FormData() + formData.append('role_id', formValues.role_id) + formData.append('role_name', formValues.role_name) + formData.append('res_ids', '2,3') + + return postRoleForm(formData) + }, + + saveOrUpdateAccount: async (formValues) => { + const { userId } = usingStorage() + const formData = new FormData() + formData.append('wu_id', formValues.userId) + formData.append('lmi_sn', formValues.lmi_sn) + formData.append('lmi2_sn', formValues.lmi2_sn) + formData.append('user_name', formValues.username) + formData.append('real_name', formValues.realname) + formData.append('email', formValues.email) + + formData.append('travel_agency_id', formValues.travelAgencyId) + formData.append('roles', formValues.roleId) + + formData.append('opi_sn', userId) + + return postAccountForm(formData) + }, + + searchAccountByCriteria: async (formValues) => { + + const searchParams = { + username: formValues.username, + realname: formValues.realname, + lgc: 2 + } + + const resultArray = await fetchAccountList(searchParams) + + const mapAccoutList = resultArray.map((r) => { + return { + userId: r.wu_id, + lmi_sn: r.lmi_sn, + lmi2_sn: r.lmi2_sn, + username: r.user_name, + realname: r.real_name, + email: r.email, + lastLogin: r.wu_lastlogindate, + travelAgency: r.travel_agency_name, + travelAgencyId: r.travel_agency_id, + // 数据库支持逗号分隔多角色(5,6,7),目前界面只需单个。 + roleId: parseInt(r.roles), + role: r.roles_name, + } + }) + + set(() => ({ + accountList: mapAccoutList + })) + }, +})) + +export default useAccountStore \ No newline at end of file diff --git a/src/stores/Airticket.js b/src/stores/Airticket.js index 1ddc720..6a393b9 100644 --- a/src/stores/Airticket.js +++ b/src/stores/Airticket.js @@ -8,8 +8,10 @@ const airTicketStore = create((set, get) => ({ loading: false, setLoading: loading => set({ loading }), setPlanList: planList => set({ planList }), + setPlanDetail: planDetail => set({ planDetail }), + setGuestList: guestList => set({ guestList }), - async getPlanList(vei_sn, GRI_Name, PNR, TimeStart, TimeEnd) { + async getPlanList(vei_sn, GRI_Name, TimeStart, TimeEnd) { const { setLoading, setPlanList } = get(); setLoading(true); const searchParams = { @@ -17,7 +19,6 @@ const airTicketStore = create((set, get) => ({ FlightDate1: TimeStart, FlightDate2: TimeEnd, GRI_Name: GRI_Name, - PNR: PNR, }; const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/GetFlightPlan`, searchParams); @@ -25,6 +26,27 @@ const airTicketStore = create((set, get) => ({ setPlanList(_result); setLoading(false); }, + + async getPlanDetail(vei_sn, gri_sn) { + const { setPlanDetail } = get(); + const searchParams = { + vei_sn: 6376, //vei_sn, + gri_sn: 369040, //gri_sn + }; + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/GetFlightPlanDetail`, searchParams); + const _result = errcode !== 0 ? [] : result; + setPlanDetail(_result); + }, + async getGuestList(coli_sn) { + const { setGuestList } = get(); + const searchParams = { + COLI_SN: 1097829, //coli_sn, + }; + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/GetFlightGuestInfo`, searchParams); + const _result = errcode !== 0 ? [] : result; + setGuestList(_result); + }, + })); export default airTicketStore; diff --git a/src/stores/Auth.js b/src/stores/Auth.js index 5511e6b..c82a3c5 100644 --- a/src/stores/Auth.js +++ b/src/stores/Auth.js @@ -55,13 +55,13 @@ const useAuthStore = create((set, get) => ({ // 以下是权限列表从数据库读取后使用的方法 // return this.permissionList.some((value, key, arry) => { // if (value.indexOf(WILDCARD_TOKEN) > -1) { - // return true; + // return true // } // if (value === perm) { - // return true; + // return true // } - // return false; - // }); + // return false + // }) }, @@ -155,19 +155,4 @@ const useAuthStore = create((set, get) => ({ }, })) -export default useAuthStore - -export class Auth { - // TODO: 等待所有获取用户信息修改完后删除 - login = { - token: '', - userId: 0, // LMI_SN - username: '0', - travelAgencyId: 0, // VEI_SN - travelAgencyName: '', - telephone: '', - emailAddress: '', - cityId: 0, - timeout: false - } -} +export default useAuthStore \ No newline at end of file diff --git a/src/stores/Root.js b/src/stores/Root.js deleted file mode 100644 index a24bd54..0000000 --- a/src/stores/Root.js +++ /dev/null @@ -1,39 +0,0 @@ -import { makeAutoObservable } from "mobx"; -import { Auth } from "./Auth"; - -class Root { - constructor() { - this.authStore = new Auth(this); - makeAutoObservable(this); - } - - clearSession() { - if (window.sessionStorage) { - const sessionStorage = window.sessionStorage; - sessionStorage.clear(); - } else { - console.error('browser not support sessionStorage!'); - } - } - - getSession(key) { - if (window.sessionStorage) { - const sessionStorage = window.sessionStorage; - return sessionStorage.getItem(key); - } else { - console.error('browser not support sessionStorage!'); - return null; - } - } - - putSession(key, value) { - if (window.sessionStorage) { - const sessionStorage = window.sessionStorage; - return sessionStorage.setItem(key, value); - } else { - console.error('browser not support sessionStorage!'); - } - } -} - -export default Root; diff --git a/src/stores/StoreContext.js b/src/stores/StoreContext.js deleted file mode 100644 index aca4a62..0000000 --- a/src/stores/StoreContext.js +++ /dev/null @@ -1,7 +0,0 @@ -import { createContext, useContext } from "react"; - -export const StoreContext = createContext(); - -export function useStore() { - return useContext(StoreContext); -} \ No newline at end of file diff --git a/src/stores/ThemeContext.js b/src/stores/ThemeContext.js new file mode 100644 index 0000000..f4f7aa7 --- /dev/null +++ b/src/stores/ThemeContext.js @@ -0,0 +1,7 @@ +import { createContext, useContext } from 'react' + +export const ThemeContext = createContext({}) + +export function useThemeContext() { + return useContext(ThemeContext) +} \ No newline at end of file diff --git a/src/utils/commons.js b/src/utils/commons.js index c5508ca..2e83226 100644 --- a/src/utils/commons.js +++ b/src/utils/commons.js @@ -129,7 +129,7 @@ export function escape2Html(str) { var output = temp.innerText || temp.textContent; temp = null; return output; - } +} export function formatPrice(price) { return Math.ceil(price).toLocaleString(); @@ -139,7 +139,6 @@ export function formatPercent(number) { return Math.round(number * 100) + "%"; } - /** * ! 不支持计算 Set 或 Map * @param {*} val @@ -148,23 +147,23 @@ export function formatPercent(number) { * false if: 'false', 'undefined' */ export function isEmpty(val) { - // return val === undefined || val === null || val === ""; - return [Object, Array].includes((val || {}).constructor) && !Object.entries(val || {}).length; + // return val === undefined || val === null || val === ""; + return [Object, Array].includes((val || {}).constructor) && !Object.entries(val || {}).length; } /** * 数组排序 */ -export const sortBy = (key) => { - return (a, b) => (a[key] > b[key] ? 1 : b[key] > a[key] ? -1 : 0); +export const sortBy = key => { + return (a, b) => (a[key] > b[key] ? 1 : b[key] > a[key] ? -1 : 0); }; /** * Object排序keys */ -export const sortKeys = (obj) => - Object.keys(obj) - .sort() - .reduce((a, k2) => ({ ...a, [k2]: obj[k2] }), {}); +export const sortKeys = obj => + Object.keys(obj) + .sort() + .reduce((a, k2) => ({ ...a, [k2]: obj[k2] }), {}); /** * 数组排序, 给定排序数组 @@ -174,41 +173,41 @@ export const sortKeys = (obj) => * @returns */ export const sortArrayByOrder = (items, keyName, keyOrder) => { - return items.sort((a, b) => { - return keyOrder.indexOf(a[keyName]) - keyOrder.indexOf(b[keyName]); - }); + return items.sort((a, b) => { + return keyOrder.indexOf(a[keyName]) - keyOrder.indexOf(b[keyName]); + }); }; /** * 合并Object, 递归地 */ export function merge(...objects) { - const isDeep = objects.some((obj) => obj !== null && typeof obj === 'object'); + const isDeep = objects.some(obj => obj !== null && typeof obj === "object"); - const result = objects[0] || (isDeep ? {} : objects[0]); + const result = objects[0] || (isDeep ? {} : objects[0]); - for (let i = 1; i < objects.length; i++) { - const obj = objects[i]; + for (let i = 1; i < objects.length; i++) { + const obj = objects[i]; - if (!obj) continue; + if (!obj) continue; - Object.keys(obj).forEach((key) => { - const val = obj[key]; + Object.keys(obj).forEach(key => { + const val = obj[key]; - if (isDeep) { - if (Array.isArray(val)) { - result[key] = [].concat(Array.isArray(result[key]) ? result[key] : [result[key]], val); - } else if (typeof val === 'object') { - result[key] = merge(result[key], val); - } else { - result[key] = val; - } - } else { - result[key] = typeof val === 'boolean' ? val : result[key]; - } - }); - } + if (isDeep) { + if (Array.isArray(val)) { + result[key] = [].concat(Array.isArray(result[key]) ? result[key] : [result[key]], val); + } else if (typeof val === "object") { + result[key] = merge(result[key], val); + } else { + result[key] = val; + } + } else { + result[key] = typeof val === "boolean" ? val : result[key]; + } + }); + } - return result; + return result; } /** @@ -217,16 +216,16 @@ export function merge(...objects) { * @see https://www.lodashjs.com/docs/lodash.groupBy#_groupbycollection-iteratee_identity */ export function groupBy(array = [], callback) { - return array.reduce((groups, item) => { - const key = typeof callback === 'function' ? callback(item) : item[callback]; + return array.reduce((groups, item) => { + const key = typeof callback === "function" ? callback(item) : item[callback]; - if (!groups[key]) { - groups[key] = []; - } + if (!groups[key]) { + groups[key] = []; + } - groups[key].push(item); - return groups; - }, {}); + groups[key].push(item); + return groups; + }, {}); } /** @@ -235,12 +234,12 @@ export function groupBy(array = [], callback) { * @param {array} keys */ export function pick(object, keys) { - return keys.reduce((obj, key) => { - if (object && Object.prototype.hasOwnProperty.call(object, key)) { - obj[key] = object[key]; - } - return obj; - }, {}); + return keys.reduce((obj, key) => { + if (object && Object.prototype.hasOwnProperty.call(object, key)) { + obj[key] = object[key]; + } + return obj; + }, {}); } /** @@ -250,44 +249,44 @@ export function pick(object, keys) { * @returns */ export function omit(object, keysToOmit) { - return Object.fromEntries(Object.entries(object).filter(([key]) => !keysToOmit.includes(key))); + return Object.fromEntries(Object.entries(object).filter(([key]) => !keysToOmit.includes(key))); } /** * 深拷贝 */ export function cloneDeep(value) { - // return structuredClone(value); - if (typeof value !== 'object' || value === null) { - return value; - } + // return structuredClone(value); + if (typeof value !== "object" || value === null) { + return value; + } - const result = Array.isArray(value) ? [] : {}; + const result = Array.isArray(value) ? [] : {}; - for (const key in value) { - if (Object.prototype.hasOwnProperty.call(value, key)) { - result[key] = cloneDeep(value[key]); - } - } + for (const key in value) { + if (Object.prototype.hasOwnProperty.call(value, key)) { + result[key] = cloneDeep(value[key]); + } + } - return result; + return result; } /** * 向零四舍五入, 固定精度设置 */ function curriedFix(precision = 0) { - return function (number) { - // Shift number by precision places - const shift = Math.pow(10, precision); - const shiftedNumber = number * shift; + return function (number) { + // Shift number by precision places + const shift = Math.pow(10, precision); + const shiftedNumber = number * shift; - // Round to nearest integer - const roundedNumber = Math.round(shiftedNumber); + // Round to nearest integer + const roundedNumber = Math.round(shiftedNumber); - // Shift back decimal place - return roundedNumber / shift; - }; + // Shift back decimal place + return roundedNumber / shift; + }; } /** * 向零四舍五入, 保留2位小数 @@ -313,106 +312,106 @@ export const fixToInt = curriedFix(0); * */ export function objectMapper(input, keyMap) { - // Loop through array mapping - if (Array.isArray(input)) { - return input.map((obj) => objectMapper(obj, keyMap)); - } - - if (typeof input === 'object') { - const mappedObj = {}; - - Object.keys(input).forEach((key) => { - // Keep original keys not in keyMap - if (!keyMap[key]) { - mappedObj[key] = input[key]; - } - // Handle array of maps - if (Array.isArray(keyMap[key])) { - keyMap[key].forEach((map) => { - let value = input[key]; - if (map.transform) value = map.transform(value); - mappedObj[map.key] = value; - }); - - // Handle single map - } else { - const map = keyMap[key]; - if (map) { - let value = input[key]; - if (map.transform) value = map.transform(value); - mappedObj[map.key || map] = value; - } - } - }); - - return mappedObj; - } - - return input; + // Loop through array mapping + if (Array.isArray(input)) { + return input.map(obj => objectMapper(obj, keyMap)); + } + + if (typeof input === "object") { + const mappedObj = {}; + + Object.keys(input).forEach(key => { + // Keep original keys not in keyMap + if (!keyMap[key]) { + mappedObj[key] = input[key]; + } + // Handle array of maps + if (Array.isArray(keyMap[key])) { + keyMap[key].forEach(map => { + let value = input[key]; + if (map.transform) value = map.transform(value); + mappedObj[map.key] = value; + }); + + // Handle single map + } else { + const map = keyMap[key]; + if (map) { + let value = input[key]; + if (map.transform) value = map.transform(value); + mappedObj[map.key || map] = value; + } + } + }); + + return mappedObj; + } + + return input; } /** * 创建一个对应于对象路径的值数组 */ export function at(obj, path) { - let result; - if (Array.isArray(obj)) { - // array case - const indexes = path.split('.').map((i) => parseInt(i)); - result = []; - for (let i = 0; i < indexes.length; i++) { - result.push(obj[indexes[i]]); - } - } else { - // object case - const indexes = path.split('.').map((i) => i); - result = [obj]; - for (let i = 0; i < indexes.length; i++) { - result = [result[0][indexes[i]]]; - } - } - return result; + let result; + if (Array.isArray(obj)) { + // array case + const indexes = path.split(".").map(i => parseInt(i)); + result = []; + for (let i = 0; i < indexes.length; i++) { + result.push(obj[indexes[i]]); + } + } else { + // object case + const indexes = path.split(".").map(i => i); + result = [obj]; + for (let i = 0; i < indexes.length; i++) { + result = [result[0][indexes[i]]]; + } + } + return result; } /** * 删除 null/undefined */ export function flush(collection) { - let result, len, i; - if (!collection) { - return undefined; - } - if (Array.isArray(collection)) { - result = []; - len = collection.length; - for (i = 0; i < len; i++) { - const elem = collection[i]; - if (elem != null) { - result.push(elem); - } - } - return result; - } - if (typeof collection === 'object') { - result = {}; - const keys = Object.keys(collection); - len = keys.length; - for (i = 0; i < len; i++) { - const key = keys[i]; - const value = collection[key]; - if (value != null) { - result[key] = value; - } - } - return result; - } - return undefined; + let result, len, i; + if (!collection) { + return undefined; + } + if (Array.isArray(collection)) { + result = []; + len = collection.length; + for (i = 0; i < len; i++) { + const elem = collection[i]; + if (elem != null) { + result.push(elem); + } + } + return result; + } + if (typeof collection === "object") { + result = {}; + const keys = Object.keys(collection); + len = keys.length; + for (i = 0; i < len; i++) { + const key = keys[i]; + const value = collection[key]; + if (value != null) { + result[key] = value; + } + } + return result; + } + return undefined; } /** * 千分位 格式化数字 */ -export const numberFormatter = (number) => { - return new Intl.NumberFormat().format(number); +export const numberFormatter = number => { + return new Intl.NumberFormat().format(number); }; /** @@ -422,193 +421,195 @@ export const numberFormatter = (number) => { * getNestedValue(obj, keyArr); // Returns: 'c' */ export const getNestedValue = (obj, keyArr) => { - return keyArr.reduce((acc, curr) => { - return acc && Object.prototype.hasOwnProperty.call(acc, curr) ? acc[curr] : undefined; - // return acc && acc[curr]; - }, obj); + return keyArr.reduce((acc, curr) => { + return acc && Object.prototype.hasOwnProperty.call(acc, curr) ? acc[curr] : undefined; + // return acc && acc[curr]; + }, obj); }; /** * 计算笛卡尔积 */ -export const cartesianProductArray = (arr, sep = '_', index = 0, prefix = '') => { - let result = []; - if (index === arr.length) { - return [prefix]; - } - arr[index].forEach((item) => { - result = result.concat(cartesianProductArray(arr, sep, index + 1, prefix ? `${prefix}${sep}${item}` : `${item}`)); - }); - return result; +export const cartesianProductArray = (arr, sep = "_", index = 0, prefix = "") => { + let result = []; + if (index === arr.length) { + return [prefix]; + } + arr[index].forEach(item => { + result = result.concat(cartesianProductArray(arr, sep, index + 1, prefix ? `${prefix}${sep}${item}` : `${item}`)); + }); + return result; }; -export const stringToColour = (str) => { - var hash = 0 - for (let i = 0; i < str.length; i++) { - hash = str.charCodeAt(i) + ((hash << 5) - hash) - } - var colour = '#' - for (let i = 0; i < 3; i++) { - var value = (hash >> (i * 8)) & 0xff - value = (value % 150) + 50 - colour += ('00' + value.toString(16)).substr(-2) - } - return colour -} +export const stringToColour = str => { + var hash = 0; + for (let i = 0; i < str.length; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + } + var colour = "#"; + for (let i = 0; i < 3; i++) { + var value = (hash >> (i * 8)) & 0xff; + value = (value % 150) + 50; + colour += ("00" + value.toString(16)).substr(-2); + } + return colour; +}; export const debounce = (func, wait, immediate) => { - var timeout; - return function () { - var context = this, - args = arguments; - clearTimeout(timeout); - if (immediate && !timeout) func.apply(context, args); - timeout = setTimeout(function () { - timeout = null; - if (!immediate) func.apply(context, args); - }, wait); - }; -} + var timeout; + return function () { + var context = this, + args = arguments; + clearTimeout(timeout); + if (immediate && !timeout) func.apply(context, args); + timeout = setTimeout(function () { + timeout = null; + if (!immediate) func.apply(context, args); + }, wait); + }; +}; -export const removeFormattingChars = (str) => { - const regex = /[\r\n\t\v\f]/g; - str = str.replace(regex, ' '); - // Replace more than four consecutive spaces with a single space - str = str.replace(/\s{4,}/g, ' '); - return str; -} +export const removeFormattingChars = str => { + const regex = /[\r\n\t\v\f]/g; + str = str.replace(regex, " "); + // Replace more than four consecutive spaces with a single space + str = str.replace(/\s{4,}/g, " "); + return str; +}; export const olog = (text, ...args) => { - console.log( - `%c ${text} `, - 'background:#fb923c ; padding: 1px; border-radius: 3px; color: #fff',...args - ); + console.log(`%c ${text} `, "background:#fb923c ; padding: 1px; border-radius: 3px; color: #fff", ...args); }; -export const sanitizeFilename = (str) => { - // Remove whitespace and replace with hyphens - str = str.replace(/\s+/g, '-'); - // Remove invalid characters and replace with hyphens - str = str.replace(/[^a-zA-Z0-9.-]/g, '-'); - // Replace consecutive hyphens with a single hyphen - str = str.replace(/-+/g, '-'); - // Trim leading and trailing hyphens - str = str.replace(/^-+|-+$/g, ''); - return str; -} +export const sanitizeFilename = str => { + // Remove whitespace and replace with hyphens + str = str.replace(/\s+/g, "-"); + // Remove invalid characters and replace with hyphens + str = str.replace(/[^a-zA-Z0-9.-]/g, "-"); + // Replace consecutive hyphens with a single hyphen + str = str.replace(/-+/g, "-"); + // Trim leading and trailing hyphens + str = str.replace(/^-+|-+$/g, ""); + return str; +}; export const formatBytes = (bytes, decimals = 2) => { - if (bytes === 0) return ''; + if (bytes === 0) return ""; - const k = 1024; - const dm = decimals < 0 ? 0 : decimals; - const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i]; }; export const calcCacheSizes = async () => { - try { - let swCacheSize = 0; - let diskCacheSize = 0; - let indexedDBSize = 0; - - // 1. Get the service worker cache size - if ('caches' in window) { - const cacheNames = await caches.keys(); - for (const name of cacheNames) { - const cache = await caches.open(name); - const requests = await cache.keys(); - for (const request of requests) { - const response = await cache.match(request); - swCacheSize += Number(response.headers.get('Content-Length')) || 0; - } - } - } - - // 2. Get the disk cache size - // const diskCacheName = 'disk-cache'; - // const diskCache = await caches.open(diskCacheName); - // const diskCacheKeys = await diskCache.keys(); - // for (const request of diskCacheKeys) { - // const response = await diskCache.match(request); - // diskCacheSize += Number(response.headers.get('Content-Length')) || 0; - // } - - // 3. Get the IndexedDB cache size - // const indexedDBNames = await window.indexedDB.databases(); - // for (const dbName of indexedDBNames) { - // const db = await window.indexedDB.open(dbName.name); - // const objectStoreNames = db.objectStoreNames; - - // if (objectStoreNames !== undefined) { - // const objectStores = Array.from(objectStoreNames).map((storeName) => db.transaction([storeName], 'readonly').objectStore(storeName)); - - // for (const objectStore of objectStores) { - // const request = objectStore.count(); - // request.onsuccess = () => { - // indexedDBSize += request.result; - // }; - // } - // } - // } - - return { swCacheSize, diskCacheSize, indexedDBSize, totalSize: Number(swCacheSize) + Number(diskCacheSize) + indexedDBSize }; - } catch (error) { - console.error('Error getting cache sizes:', error); - } + try { + let swCacheSize = 0; + let diskCacheSize = 0; + let indexedDBSize = 0; + + // 1. Get the service worker cache size + if ("caches" in window) { + const cacheNames = await caches.keys(); + for (const name of cacheNames) { + const cache = await caches.open(name); + const requests = await cache.keys(); + for (const request of requests) { + const response = await cache.match(request); + swCacheSize += Number(response.headers.get("Content-Length")) || 0; + } + } + } + + // 2. Get the disk cache size + // const diskCacheName = 'disk-cache'; + // const diskCache = await caches.open(diskCacheName); + // const diskCacheKeys = await diskCache.keys(); + // for (const request of diskCacheKeys) { + // const response = await diskCache.match(request); + // diskCacheSize += Number(response.headers.get('Content-Length')) || 0; + // } + + // 3. Get the IndexedDB cache size + // const indexedDBNames = await window.indexedDB.databases(); + // for (const dbName of indexedDBNames) { + // const db = await window.indexedDB.open(dbName.name); + // const objectStoreNames = db.objectStoreNames; + + // if (objectStoreNames !== undefined) { + // const objectStores = Array.from(objectStoreNames).map((storeName) => db.transaction([storeName], 'readonly').objectStore(storeName)); + + // for (const objectStore of objectStores) { + // const request = objectStore.count(); + // request.onsuccess = () => { + // indexedDBSize += request.result; + // }; + // } + // } + // } + + return { swCacheSize, diskCacheSize, indexedDBSize, totalSize: Number(swCacheSize) + Number(diskCacheSize) + indexedDBSize }; + } catch (error) { + console.error("Error getting cache sizes:", error); + } }; -export const clearAllCaches = async (cb) => { - try { - // 1. Clear the service worker cache - if ('caches' in window) { - // if (navigator.serviceWorker) { - const cacheNames = await caches.keys(); - await Promise.all(cacheNames.map((name) => caches.delete(name))); - } - - // 2. Clear the disk cache (HTTP cache) - // const diskCacheName = 'disk-cache'; - // await window.caches.delete(diskCacheName); - // const diskCache = await window.caches.open(diskCacheName); - // const diskCacheKeys = await diskCache.keys(); - // await Promise.all(diskCacheKeys.map((request) => diskCache.delete(request))); - - // 3. Clear the IndexedDB cache - const indexedDBNames = await window.indexedDB.databases(); - await Promise.all(indexedDBNames.map((dbName) => window.indexedDB.deleteDatabase(dbName.name))); - - // Unregister the service worker - const registration = await navigator.serviceWorker.getRegistration(); - if (registration) { - await registration.unregister(); - console.log('Service worker unregistered'); - } else { - console.log('No service worker registered'); - } - if (typeof cb === 'function' ) { - cb(); - } - - } catch (error) { - console.error('Error clearing caches or unregistering service worker:', error); - } +export const clearAllCaches = async cb => { + try { + // 1. Clear the service worker cache + if ("caches" in window) { + // if (navigator.serviceWorker) { + const cacheNames = await caches.keys(); + await Promise.all(cacheNames.map(name => caches.delete(name))); + } + + // 2. Clear the disk cache (HTTP cache) + // const diskCacheName = 'disk-cache'; + // await window.caches.delete(diskCacheName); + // const diskCache = await window.caches.open(diskCacheName); + // const diskCacheKeys = await diskCache.keys(); + // await Promise.all(diskCacheKeys.map((request) => diskCache.delete(request))); + + // 3. Clear the IndexedDB cache + const indexedDBNames = await window.indexedDB.databases(); + await Promise.all(indexedDBNames.map(dbName => window.indexedDB.deleteDatabase(dbName.name))); + + // Unregister the service worker + const registration = await navigator.serviceWorker.getRegistration(); + if (registration) { + await registration.unregister(); + console.log("Service worker unregistered"); + } else { + console.log("No service worker registered"); + } + if (typeof cb === "function") { + cb(); + } + } catch (error) { + console.error("Error clearing caches or unregistering service worker:", error); + } }; -export const loadScript = (src) => { - return new Promise((resolve, reject) => { - const script = document.createElement('script'); - script.type = 'text/javascript'; - script.onload = resolve; - script.onerror = reject; - script.crossOrigin = 'anonymous'; - script.src = src; - if (document.head.append) { - document.head.append(script); - } else { - document.getElementsByTagName('head')[0].appendChild(script); - } - }); +export const loadScript = src => { + return new Promise((resolve, reject) => { + const script = document.createElement("script"); + script.type = "text/javascript"; + script.onload = resolve; + script.onerror = reject; + script.crossOrigin = "anonymous"; + script.src = src; + if (document.head.append) { + document.head.append(script); + } else { + document.getElementsByTagName("head")[0].appendChild(script); + } + }); }; +//格式化为冒号时间,2010转为20:10 +export const formatColonTime = text => { + const hours = text.substring(0, 2); + const minutes = text.substring(2); + return `${hours}:${minutes}`; +}; diff --git a/src/utils/request.js b/src/utils/request.js index 68d80a6..ac97803 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -106,6 +106,7 @@ export function postForm(url, data) { } }).then(checkStatus) .then(response => response.json()) + .then(checkBizCode) .catch(error => { throw error }) diff --git a/src/views/App.jsx b/src/views/App.jsx index 928911d..b1eb9ca 100644 --- a/src/views/App.jsx +++ b/src/views/App.jsx @@ -152,8 +152,9 @@ function App() { { label: {t('ChangePassword')}, key: '0' }, { label: {t('Profile')}, key: '1' }, { label: {t('account:management.tile')}, key: '3' }, + { label: {t('account:management.roleList')}, key: '4' }, { type: 'divider' }, - { label: {t('Logout')}, key: '4' }, + { label: {t('Logout')}, key: '99' }, ], { type: 'divider' }, { label: <>v{BUILD_VERSION}, key: 'BUILD_VERSION' }, diff --git a/src/views/account/Management.jsx b/src/views/account/Management.jsx index 4f028ea..6c2c323 100644 --- a/src/views/account/Management.jsx +++ b/src/views/account/Management.jsx @@ -3,72 +3,18 @@ import { Row, Col, Space, Button, Table, Select, TreeSelect, Typography, Modal, import { ExclamationCircleFilled } from '@ant-design/icons' import { useTranslation } from 'react-i18next' import useFormStore from '@/stores/Form' -import useReservationStore from '@/stores/Reservation' +import useAuthStore from '@/stores/Auth' +import useAccountStore from '@/stores/Account' +import { fetchRoleList } from '@/stores/Account' import SearchForm from '@/components/SearchForm' +import RequireAuth from '@/components/RequireAuth' +import { PERM_ROLE_NEW } from '@/config' const { Title } = Typography -const permissionData = [ - { - title: '机票管理', - value: '0-0', - key: '0-0', - children: [ - { - title: '录入机票价格', - value: '0-0-0', - key: '0-0-0', - }, - ], - }, - { - title: '产品管理', - value: '0-1', - key: '0-1', - children: [ - { - title: '录入产品价格', - value: '0-1-0', - key: '0-1-0', - }, - { - title: '新增产品描述', - value: '0-1-1', - key: '0-1-1', - }, - { - title: '复制供应商产品信息', - value: '0-1-2', - key: '0-1-2', - }, - ], - }, - { - title: '账号管理', - value: '2-1', - key: '2-1', - children: [ - { - title: '重置账号密码', - value: '2-1-0', - key: '2-1-0', - }, - { - title: '禁用账号', - value: '2-1-1', - key: '2-1-1', - }, - { - title: '分配账号角色', - value: '2-1-2', - key: '2-1-2', - }, - ], - }, -]; - function Management() { const { t } = useTranslation() + const accountListColumns = [ { title: t('account:username'), @@ -79,14 +25,17 @@ function Management() { title: t('account:realname'), dataIndex: 'realname', }, + { + title: t('account:travelAgency'), + dataIndex: 'travelAgency', + }, { title: t('account:email'), dataIndex: 'email', }, { title: t('account:role'), - dataIndex: 'role', - render: roleRender + dataIndex: 'role' }, { title: t('account:lastLogin'), @@ -99,139 +48,95 @@ function Management() { }, ] - function accountRender(text) { - return ( - - ) - } - - function roleRender(text) { + function accountRender(text, account) { return ( - + ) } - function actionRender() { + function actionRender(text, account) { return ( - - + + ) } - const onPermissionChange = (newValue) => { - console.log('onChange ', newValue); - setPermissionValue(newValue); - } - - const [permissionValue, setPermissionValue] = useState(['0-0-0']) const [isAccountModalOpen, setAccountModalOpen] = useState(false) - const [isRoleModalOpen, setRoleModalOpen] = useState(false) const [dataLoading, setDataLoading] = useState(false) - const [accountList, setaccountList] = useState([ - { - key: 1, - username: 'bjyiran', - realname: '怡小芳', - email: 'xiaofang@yiran.com', - role: '国内供应商', - lastLogin: '2024-06-12 13:53' - }, - { - key: 2, - username: 'int-robin', - realname: 'Robin', - email: 'robin@int.com', - role: '海外供应商', - lastLogin: '2024-06-12 13:53' - }, - { - key: 3, - username: 'betty-wu', - realname: '吴雪', - email: 'betty@hainatravel.com', - role: '客服组', - lastLogin: '2024-06-12 13:53' - }, - { - key: 4, - username: 'lancy', - realname: '吴金倩', - email: 'lancy@hainatravel.com', - role: '产品组', - lastLogin: '2024-06-12 13:53' - }, - { - key: 5, - username: 'LYJ', - realname: '廖一军', - email: 'lyj@hainatravel.com', - role: 'Web 开发组,海外测试供应商', - lastLogin: '2024-06-12 13:53' - } - ]) - - const formValuesToSub = useFormStore((state) => state.formValuesToSub) + const [roleAllList, setRoleAllList] = useState([]) - const [editAccountForm, editRoleForm] = Form.useForm() - const [fetchReservationList] = - useReservationStore((state) => - [state.fetchAllGuideList, state.fetchReservationList, state.reservationList, state.reservationPage, state.cityList, state.selectReservation, state.getCityListByReservationId]) + const [accountForm] = Form.useForm() + const [searchAccountByCriteria, accountList, disableAccount, saveOrUpdateAccount, resetAccountPassword] = + useAccountStore((state) => + [state.searchAccountByCriteria, state.accountList, state.disableAccount, state.saveOrUpdateAccount, state.resetAccountPassword]) const { notification, modal } = App.useApp() - const handleAccountOk = () => { + const onAccountSeleted = async (account) => { + accountForm.setFieldsValue(account) + const roleList = await fetchRoleList() + setRoleAllList(roleList.map(r => { + return { + value: r.role_id, + label: r.role_name, + disabled: r.role_id === 1 + } + })) + setAccountModalOpen(true) } - const handleAccountCancel = () => { - setAccountModalOpen(false) - } - - const handleRoleOk = () => { - } - - const handleRoleCancel = () => { - setRoleModalOpen(false) - } - - const onFinish = (values) => { + const onAccountFinish = (values) => { console.log(values) + saveOrUpdateAccount(values) + .catch(ex => { + notification.error({ + message: 'Notification', + description: ex.message, + placement: 'top', + duration: 4, + }) + }) } - const onFinishFailed = (error) => { + const onAccountFailed = (error) => { console.log('Failed:', error) // form.resetFields() } - // 默认重新搜索第一页,所有状态的计划 - const onSearchClick = (current = 1, status = null) => { - } - - const showDisableConfirm = () => { + const showDisableConfirm = (account) => { modal.confirm({ title: 'Do you want to disable this account?', icon: , - content: 'Username: Ivy, Realname: 怡小芳', + content: `Username: ${account.username}, Realname: ${account.realname}`, onOk() { - console.log('OK') + disableAccount(account.userId) }, onCancel() { - console.log('Cancel') }, }) } - const showResetPasswordConfirm = () => { + const showResetPasswordConfirm = (account) => { + const randomPassword = account.username + (Math.floor(Math.random() * 900) + 100) modal.confirm({ title: 'Do you want to reset password?', icon: , - content: 'Username: Ivy, Realname: 怡小芳', + content: `Username: ${account.username}, Realname: ${account.realname}`, onOk() { - console.log('OK') + resetAccountPassword(account.userId, randomPassword) + .then(() => { + notification.info({ + message: '新密码:' + randomPassword, + description: `请复制密码给 [${account.realname}]`, + placement: 'top', + duration: 60, + }) + }) + console.log('ResetPassword') }, onCancel() { - console.log('Cancel') }, }) } @@ -240,137 +145,109 @@ function Management() { <> -
setAccountModalOpen(false)} onCancel={() => setAccountModalOpen(false)} + destroyOnClose={true} + clearOnDestroy={true} + modalRender={(dom) => ( + - {t('account:management.newAccount')} - - - - - - - - - - - - -
-
- {/* Role Edit */} - + )} > -
+ + + - {t('account:management.newRole')} - - - - - - - + +
+ + + + + + + + + + + +
{t('account:management.tile')} { + onSubmit={(err, formValues, filedsVal) => { + console.info(formValues) setDataLoading(true) - fetchReservationList(formVal) + searchAccountByCriteria(formValues) .catch(ex => { notification.error({ message: 'Notification', @@ -388,7 +265,6 @@ function Management() { - @@ -397,6 +273,7 @@ function Management() { 1) { + return ( + + ) + } + } + + const onPermissionChange = (newValue) => { + console.log('onChange ', newValue) + setPermissionValue(newValue) + } + + useEffect (() => { + setDataLoading(true) + fetchRoleList() + .then(r => { + setRoleAllList(r) + }) + .finally(() => { + setDataLoading(false) + }) + }, []) + + const [permissionValue, setPermissionValue] = useState(['0-0-0']) + const [isRoleModalOpen, setRoleModalOpen] = useState(false) + const [dataLoading, setDataLoading] = useState(false) + const [roleAllList, setRoleAllList] = useState([]) + + const [roleForm] = Form.useForm() + const [saveOrUpdateRole, newRole] = + useAccountStore((state) => + [state.saveOrUpdateRole, state.newRole]) + + const { notification, modal } = App.useApp() + + const onRoleSeleted = (role) => { + roleForm.setFieldsValue(role) + setRoleModalOpen(true) + } + + const onNewRole = () => { + const role = newRole() + roleForm.setFieldsValue(role) + setRoleModalOpen(true) + } + + const onRoleFinish = (values) => { + console.log(values) + saveOrUpdateRole(values) + .catch(ex => { + console.info(ex.message) + notification.error({ + message: 'Notification', + description: ex.message, + placement: 'top', + duration: 4, + }) + }) + } + + const onRoleFailed = (error) => { + console.log('Failed:', error) + // form.resetFields() + } + + return ( + <> + setRoleModalOpen(false)} onCancel={() => setRoleModalOpen(false)} + destroyOnClose={true} + clearOnDestroy={true} + modalRender={(dom) => ( +
+ {dom} + + )} + > + + + + + + + +
+ + {t('account:management.roleList')} + + + + + + + + + + + +
{ return t('Total') + `:${total}` } + }} + onChange={(pagination) => { onSearchClick(pagination.current) }} + columns={roleListColumns} dataSource={roleAllList} + /> + + + + + ) +} + +export default RoleList diff --git a/src/views/airticket/Index.jsx b/src/views/airticket/Index.jsx index 06cbb02..32a84a5 100644 --- a/src/views/airticket/Index.jsx +++ b/src/views/airticket/Index.jsx @@ -1,8 +1,8 @@ import { useState, useEffect } from "react"; import { Grid, Divider, Layout, Spin, Input, Col, Row, Space, List, Table } from "antd"; import { PhoneOutlined, CustomerServiceOutlined, AudioOutlined } from "@ant-design/icons"; -import { useParams, useHref, useNavigate } from "react-router-dom"; -import { isEmpty } from "@/utils/commons"; +import { useParams, useHref, useNavigate, NavLink } from "react-router-dom"; +import { isEmpty, formatColonTime } from "@/utils/commons"; import dayjs from "dayjs"; import SearchForm from "@/components/SearchForm"; @@ -29,6 +29,11 @@ const planListColumns = [ title: "出发日期", key: "StartDate", dataIndex: "StartDate", + sorter: (a, b) => { + const dateA = new Date(a.StartDate); + const dateB = new Date(b.StartDate); + return dateB.getTime() - dateA.getTime(); + }, }, { title: "出发城市", @@ -49,16 +54,25 @@ const planListColumns = [ title: "起飞时间", key: "FlightTimeStart", dataIndex: "FlightTimeStart", + render: text => formatColonTime(text), }, { title: "落地时间", key: "FlightTimeEnd", dataIndex: "FlightTimeEnd", + render: text => formatColonTime(text), }, { - title: "PNR 暂时用FlightInfo", + title: "是否出票", + key: "COLD_PlanVEI_SN", + dataIndex: "COLD_PlanVEI_SN", + render: (text, record) => "否", + }, + { + title: "操作", key: "FlightInfo", dataIndex: "FlightInfo", + render: (text, record) => {"编辑"}, }, ]; @@ -66,7 +80,7 @@ const Airticket = props => { const href = useHref(); const navigate = useNavigate(); const { phonenumber } = useParams(); - const {travelAgencyId, } = usingStorage(); + const { travelAgencyId } = usingStorage(); const [getPlanList, planList, loading] = airTicketStore(state => [state.getPlanList, state.planList, state.loading]); const [phone_number, setPhone_number] = useState(phonenumber); const showTotal = total => `合计 ${total} `; @@ -82,14 +96,14 @@ const Airticket = props => { dates: [dayjs().startOf("M"), dayjs().endOf("M")], }} fieldsConfig={{ - shows: ["referenceNo", "PNR", "dates"], + shows: ["referenceNo", "dates"], fieldProps: { referenceNo: { label: "搜索计划" }, dates: { label: "出发日期" }, }, }} onSubmit={(err, formVal, filedsVal) => { - getPlanList(travelAgencyId, formVal.referenceNo, formVal.PNR, formVal.startdate, formVal.endtime); + getPlanList(travelAgencyId, formVal.referenceNo, formVal.startdate, formVal.endtime); }} /> diff --git a/src/views/airticket/Plan.jsx b/src/views/airticket/Plan.jsx new file mode 100644 index 0000000..29ba956 --- /dev/null +++ b/src/views/airticket/Plan.jsx @@ -0,0 +1,196 @@ +import { useState, useEffect } from "react"; +import { Grid, Divider, Layout, Form, Input, Col, Row, Space, Collapse, Table, Button } from "antd"; +import { PhoneOutlined, CustomerServiceOutlined, AudioOutlined, ArrowUpOutlined, ArrowDownOutlined } from "@ant-design/icons"; +import { useParams, useHref, useNavigate, NavLink } from "react-router-dom"; +import { isEmpty, formatColonTime } from "@/utils/commons"; +import { OFFICEWEBVIEWERURL } from "@/config"; + +import airTicketStore from "@/stores/Airticket"; +import { usingStorage } from "@/hooks/usingStorage"; + +const AirticketPlan = props => { + const { coli_sn } = useParams(); + const { travelAgencyId, loginToken } = usingStorage(); + const [getPlanDetail, planDetail, getGuestList, guestList, loading] = airTicketStore(state => [state.getPlanDetail, state.planDetail, state.getGuestList, state.guestList, state.loading]); + const reservationUrl = `https://p9axztuwd7x8a7.mycht.cn/Service_BaseInfoWeb/FlightPlanDocx?GRI_SN=${coli_sn}&VEI_SN=${travelAgencyId}`; + const reservationPreviewUrl = OFFICEWEBVIEWERURL + encodeURIComponent(reservationUrl); + + console.log(reservationPreviewUrl); + + const Airticket_form = props => { + const aitInfo = props.airInfo; + return ( + <> +
+ + + + + + + } value={aitInfo.FromCity} /> + } value={aitInfo.ToCity} /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {/* + + */} + + +
+ + ); + }; + + const detail_items = () => { + return planDetail + ? planDetail.map(item => { + return { + key: item.id, + label: `${item.StartDate} 计划: ${item.FlightInfo}`, + children: , + }; + }) + : []; + }; + + const guestListColumns = [ + { + title: "姓名", + key: "MEI_Name", + dataIndex: "MEI_Name", + }, + { + title: "证件类型", + key: "MEI_PassportType", + dataIndex: "MEI_PassportType", + }, + { + title: "证件号", + key: "MEI_PassportNo", + dataIndex: "MEI_PassportNo", + }, + { + title: "证件有效期", + key: "MEI_PassportValidDate", + dataIndex: "MEI_PassportValidDate", + }, + { + title: "性别", + key: "MEI_Gender", + dataIndex: "MEI_Gender", + }, + { + title: "年龄", + key: "MEI_age", + dataIndex: "MEI_age", + }, + { + title: "国籍", + key: "MEI_Country", + dataIndex: "MEI_Country", + }, + { + title: "票号", + key: "MEI_SN", + dataIndex: "MEI_SN", + }, + { + title: "PNR", + key: "MEI_SN", + dataIndex: "MEI_SN", + }, + { + title: "机票费用(RMB)含基建和税", + key: "MEI_SN", + dataIndex: "MEI_SN", + }, + { + title: "折扣", + key: "MEI_SN", + dataIndex: "MEI_SN", + }, + { + title: "手续费", + key: "MEI_SN", + dataIndex: "MEI_SN", + }, + { + title: "机票类型(成人/儿童/婴儿)", + key: "MEI_SN", + dataIndex: "MEI_SN", + }, + ]; + + useEffect(() => { + getPlanDetail(travelAgencyId, coli_sn); + getGuestList(travelAgencyId, coli_sn); + console.log(detail_items()); + }, []); + + return ( + + + + {/* */} + + + 出票信息 + + + + + + ); +}; +export default AirticketPlan;