From 749084f0aaa72352245e81b92a568d9e73321c35 Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Mon, 17 Jun 2024 10:08:45 +0800 Subject: [PATCH 01/24] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=9D=83?= =?UTF-8?q?=E9=99=90=E8=B7=AF=E7=94=B1=E7=BB=84=E4=BB=B6=EF=BC=8C=E8=BF=98?= =?UTF-8?q?=E6=9C=89=E8=B4=A6=E5=8F=B7=E7=AE=A1=E7=90=86=E5=B8=B8=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ErrorPage.jsx | 6 +++--- src/components/RequireAuth.jsx | 19 +++++++++++++++++++ src/config.js | 7 +++++++ src/main.jsx | 5 ++++- src/views/account/Management.jsx | 6 +++--- 5 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 src/components/RequireAuth.jsx 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..24bbdae --- /dev/null +++ b/src/components/RequireAuth.jsx @@ -0,0 +1,19 @@ +import { Result } from 'antd' +import useAuthStore from '@/stores/Auth' + +export default function RequireAuth({ children, ...props }, ) { + + const isPermitted = useAuthStore((state) => state.isPermitted) + + if (isPermitted(props.subject)) { + return children + } else { + return ( + + ) + } +} \ No newline at end of file diff --git a/src/config.js b/src/config.js index 58e13a2..b6e379a 100644 --- a/src/config.js +++ b/src/config.js @@ -9,3 +9,10 @@ export const SMALL_DATETIME_FORMAT = "YYYY-MM-DD 23:59"; const __BUILD_VERSION__ = `__BUILD_VERSION__`.replace(/"/g, '') export const BUILD_VERSION = import.meta.env.PROD ? __BUILD_VERSION__ : import.meta.env.MODE; + +// 权限常量定义 +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' diff --git a/src/main.jsx b/src/main.jsx index ff392ac..a5c8525 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -14,6 +14,7 @@ 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"; @@ -31,6 +32,8 @@ import InvoicePaid from "@/views/invoice/Paid"; import InvoicePaidDetail from "@/views/invoice/PaidDetail"; import Airticket from "@/views/airticket/Index"; +import { PERM_ACCOUNT_MANAGEMENT } from '@/config' + import './i18n'; configure({ @@ -53,7 +56,7 @@ const router = createBrowserRouter([ { path: "reservation/:reservationId", element: }, { path: "account/change-password", element: }, { path: "account/profile", element: }, - { path: "account/management", element: }, + { path: "account/management", element: }, { path: "feedback", element: }, { path: "feedback/:GRI_SN/:CII_SN/:RefNo", element: }, { path: "feedback/:GRI_SN/:RefNo", element: }, diff --git a/src/views/account/Management.jsx b/src/views/account/Management.jsx index 4f028ea..5f063e0 100644 --- a/src/views/account/Management.jsx +++ b/src/views/account/Management.jsx @@ -65,7 +65,7 @@ const permissionData = [ }, ], }, -]; +] function Management() { const { t } = useTranslation() @@ -121,8 +121,8 @@ function Management() { } const onPermissionChange = (newValue) => { - console.log('onChange ', newValue); - setPermissionValue(newValue); + console.log('onChange ', newValue) + setPermissionValue(newValue) } const [permissionValue, setPermissionValue] = useState(['0-0-0']) From 849e6ceef0fb845a86c55147a5230f9a9443e104 Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Mon, 17 Jun 2024 10:59:14 +0800 Subject: [PATCH 02/24] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=9D=83?= =?UTF-8?q?=E9=99=90=E6=8E=A7=E5=88=B6=E7=BB=84=E4=BB=B6=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/RequireAuth.jsx | 3 ++- src/main.jsx | 2 +- src/views/account/Management.jsx | 11 ++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/components/RequireAuth.jsx b/src/components/RequireAuth.jsx index 24bbdae..d8feee7 100644 --- a/src/components/RequireAuth.jsx +++ b/src/components/RequireAuth.jsx @@ -6,8 +6,9 @@ export default function RequireAuth({ children, ...props }, ) { const isPermitted = useAuthStore((state) => state.isPermitted) if (isPermitted(props.subject)) { + // if (props.subject === '/account/management') { return children - } else { + } else if (props.result) { return ( }, { path: "account/change-password", element: }, { path: "account/profile", element: }, - { path: "account/management", element: }, + { path: "account/management", element: }, { path: "feedback", element: }, { path: "feedback/:GRI_SN/:CII_SN/:RefNo", element: }, { path: "feedback/:GRI_SN/:RefNo", element: }, diff --git a/src/views/account/Management.jsx b/src/views/account/Management.jsx index 5f063e0..d98dbd8 100644 --- a/src/views/account/Management.jsx +++ b/src/views/account/Management.jsx @@ -3,8 +3,11 @@ 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 useAuthStore from '@/stores/Auth' import useReservationStore from '@/stores/Reservation' import SearchForm from '@/components/SearchForm' +import RequireAuth from '@/components/RequireAuth' +import { PERM_ROLE_NEW } from '@/config' const { Title } = Typography @@ -69,6 +72,7 @@ const permissionData = [ function Management() { const { t } = useTranslation() + const accountListColumns = [ { title: t('account:username'), @@ -174,6 +178,9 @@ function Management() { const formValuesToSub = useFormStore((state) => state.formValuesToSub) + + const isPermitted = useAuthStore((state) => state.isPermitted) + const [editAccountForm, editRoleForm] = Form.useForm() const [fetchReservationList] = useReservationStore((state) => @@ -388,7 +395,9 @@ function Management() { - + + + From a7f3faac13aa91b8823bc08a8839ff9ac2a0c31b Mon Sep 17 00:00:00 2001 From: YCC Date: Mon, 17 Jun 2024 14:11:53 +0800 Subject: [PATCH 03/24] =?UTF-8?q?=E6=9C=BA=E7=A5=A8=E8=AE=A1=E5=88=92?= =?UTF-8?q?=E8=AF=A6=E7=BB=86=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.js | 1 + src/main.jsx | 2 ++ src/stores/Airticket.js | 3 +-- src/views/airticket/Index.jsx | 24 ++++++++++++++++---- src/views/airticket/Plan.jsx | 42 +++++++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 src/views/airticket/Plan.jsx diff --git a/src/config.js b/src/config.js index 58e13a2..331cf28 100644 --- a/src/config.js +++ b/src/config.js @@ -6,6 +6,7 @@ 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; diff --git a/src/main.jsx b/src/main.jsx index ff392ac..dd3e4f6 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -30,6 +30,7 @@ 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 './i18n'; @@ -65,6 +66,7 @@ const router = createBrowserRouter([ { path: "invoice/paid",element:}, { path: "invoice/paid/detail/:flid",element:}, { path: "airticket",element:}, + { path: "airticket/plan/:coli_sn",element:}, ] }, { diff --git a/src/stores/Airticket.js b/src/stores/Airticket.js index 1ddc720..d342b83 100644 --- a/src/stores/Airticket.js +++ b/src/stores/Airticket.js @@ -9,7 +9,7 @@ const airTicketStore = create((set, get) => ({ setLoading: loading => set({ loading }), setPlanList: planList => set({ planList }), - 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 +17,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); diff --git a/src/views/airticket/Index.jsx b/src/views/airticket/Index.jsx index 479975c..41f6f2c 100644 --- a/src/views/airticket/Index.jsx +++ b/src/views/airticket/Index.jsx @@ -1,7 +1,7 @@ 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 { useParams, useHref, useNavigate, NavLink } from "react-router-dom"; import { isEmpty } 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,27 @@ const planListColumns = [ title: "起飞时间", key: "FlightTimeStart", dataIndex: "FlightTimeStart", + render: text => { + const hours = text.substring(0, 2); + const minutes = text.substring(2); + return `${hours}:${minutes}`; + }, }, { title: "落地时间", key: "FlightTimeEnd", dataIndex: "FlightTimeEnd", + render: text => { + const hours = text.substring(0, 2); + const minutes = text.substring(2); + return `${hours}:${minutes}`; + }, }, { - title: "PNR 暂时用FlightInfo", + title: "操作", key: "FlightInfo", dataIndex: "FlightInfo", + render: (text, record) => {"编辑"}, }, ]; @@ -83,14 +99,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..4cd4bf0 --- /dev/null +++ b/src/views/airticket/Plan.jsx @@ -0,0 +1,42 @@ +import { useState, useEffect } from "react"; +import { Grid, Divider, Layout, Spin, Input, Col, Row, Space, List, Table, Button } from "antd"; +import { PhoneOutlined, CustomerServiceOutlined, AudioOutlined } from "@ant-design/icons"; +import { useParams, useHref, useNavigate, NavLink } from "react-router-dom"; +import { isEmpty } from "@/utils/commons"; +import dayjs from "dayjs"; +import { HT_HOST, OFFICEWEBVIEWERURL } from "@/config"; + +import airTicketStore from "@/stores/Airticket"; +import useAuthStore from "@/stores/Auth"; + +const AirticketPlan = props => { + const href = useHref(); + const navigate = useNavigate(); + const { coli_sn } = useParams(); + const [travelAgencyId, loginToken] = useAuthStore( + state => state.loginUser.travelAgencyId, + state => state.loginUser.loginToken + ); + const [getPlanList, planList, loading] = airTicketStore(state => [state.getPlanList, state.planList, state.loading]); + const [loginUser] = useAuthStore(state => [state.loginUser]); + + const reservationUrl = `https://p9axztuwd7x8a7.mycht.cn/service-fileServer/DownloadPlanDoc?GRI_SN=${coli_sn}&VEI_SN=${travelAgencyId}&token=${loginToken}&FileType=1`; + + const reservationPreviewUrl = OFFICEWEBVIEWERURL + encodeURIComponent(reservationUrl); + + useEffect(() => {}, []); + + return ( + + + + + + + + + ); +}; +export default AirticketPlan; From bfc994cd12bc84f2be62085bda79eee240575496 Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Mon, 17 Jun 2024 15:56:24 +0800 Subject: [PATCH 04/24] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E4=BB=B7?= =?UTF-8?q?=E6=A0=BC=E7=95=8C=E9=9D=A2=E5=8E=9F=E5=9E=8B=EF=BC=9B=E6=89=80?= =?UTF-8?q?=E6=9C=89=E6=B5=B7=E5=A4=96=E4=BE=9B=E5=BA=94=E5=95=86=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E5=8A=A0=E4=B8=8A=E6=9D=83=E8=AF=81=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/价格管理平台.bmpr | Bin 271360 -> 272384 bytes src/components/RequireAuth.jsx | 8 ++-- src/config.js | 14 +++++++ src/main.jsx | 28 ++++++------- src/stores/Auth.js | 8 ++-- src/views/account/Management.jsx | 68 +++++++++++++++++++++++++------ 6 files changed, 92 insertions(+), 34 deletions(-) diff --git a/doc/价格管理平台.bmpr b/doc/价格管理平台.bmpr index 2e3bd02a8100699120fbaaa86a57f39c363f07c1..a33839a8211b67d4c152a12ac5c7b00e9d030095 100644 GIT binary patch delta 49412 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≶3z#atj98ocb2DTaDx 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 CT state.isPermitted) + const { userId } = usingStorage() if (isPermitted(props.subject)) { - // if (props.subject === '/account/management') { + // if (props.subject === '/account/management1') { return children } else if (props.result) { return ( ) } diff --git a/src/config.js b/src/config.js index b6e379a..86ed5a3 100644 --- a/src/config.js +++ b/src/config.js @@ -11,8 +11,22 @@ 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 953db33..643a27e 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -32,7 +32,7 @@ import InvoicePaid from "@/views/invoice/Paid"; import InvoicePaidDetail from "@/views/invoice/PaidDetail"; import Airticket from "@/views/airticket/Index"; -import { PERM_ACCOUNT_MANAGEMENT } from '@/config' +import { PERM_ACCOUNT_MANAGEMENT, PERM_OVERSEA, PERM_AIR_TICKET } from '@/config' import './i18n'; @@ -52,22 +52,22 @@ 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: "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: }, ] }, { diff --git a/src/stores/Auth.js b/src/stores/Auth.js index f7f31f3..59e27cc 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 + // }) }, diff --git a/src/views/account/Management.jsx b/src/views/account/Management.jsx index d98dbd8..4266fa3 100644 --- a/src/views/account/Management.jsx +++ b/src/views/account/Management.jsx @@ -12,6 +12,18 @@ import { PERM_ROLE_NEW } from '@/config' const { Title } = Typography const permissionData = [ + { + title: '海外供应商', + value: 'oversea-0', + key: 'oversea-0', + children: [ + { + title: '所有海外功能', + value: 'oversea-0-0', + key: 'oversea-0-0', + }, + ], + }, { title: '机票管理', value: '0-0', @@ -29,6 +41,11 @@ const permissionData = [ value: '0-1', key: '0-1', children: [ + { + title: '搜索供应商产品', + value: 'B-1-0', + key: 'B-1-0', + }, { title: '录入产品价格', value: '0-1-0', @@ -52,19 +69,29 @@ const permissionData = [ key: '2-1', children: [ { - title: '重置账号密码', - value: '2-1-0', - key: '2-1-0', + title: '搜索账号', + value: '2-1-01', + key: '2-1-01', + }, + { + title: '新增账号', + value: '2-1-11', + key: '2-1-11', }, { title: '禁用账号', - value: '2-1-1', - key: '2-1-1', + value: '2-1-21', + key: '2-1-21', }, { - title: '分配账号角色', - value: '2-1-2', - key: '2-1-2', + title: '重置账号密码', + value: '2-1-31', + key: '2-1-31', + }, + { + title: '新增角色', + value: '2-1-41', + key: '2-1-41', }, ], }, @@ -83,6 +110,10 @@ function Management() { title: t('account:realname'), dataIndex: 'realname', }, + { + title: t('account:travelAgency'), + dataIndex: 'travelAgency', + }, { title: t('account:email'), dataIndex: 'email', @@ -138,6 +169,7 @@ function Management() { key: 1, username: 'bjyiran', realname: '怡小芳', + travelAgency: '三千界', email: 'xiaofang@yiran.com', role: '国内供应商', lastLogin: '2024-06-12 13:53' @@ -146,6 +178,7 @@ function Management() { key: 2, username: 'int-robin', realname: 'Robin', + travelAgency: 'IAT', email: 'robin@int.com', role: '海外供应商', lastLogin: '2024-06-12 13:53' @@ -154,6 +187,7 @@ function Management() { key: 3, username: 'betty-wu', realname: '吴雪', + travelAgency: '桂林国旅', email: 'betty@hainatravel.com', role: '客服组', lastLogin: '2024-06-12 13:53' @@ -162,6 +196,7 @@ function Management() { key: 4, username: 'lancy', realname: '吴金倩', + travelAgency: '海纳国旅', email: 'lancy@hainatravel.com', role: '产品组', lastLogin: '2024-06-12 13:53' @@ -170,6 +205,7 @@ function Management() { key: 5, username: 'LYJ', realname: '廖一军', + travelAgency: '海纳国际旅行社', email: 'lyj@hainatravel.com', role: 'Web 开发组,海外测试供应商', lastLogin: '2024-06-12 13:53' @@ -178,9 +214,6 @@ function Management() { const formValuesToSub = useFormStore((state) => state.formValuesToSub) - - const isPermitted = useAuthStore((state) => state.isPermitted) - const [editAccountForm, editRoleForm] = Form.useForm() const [fetchReservationList] = useReservationStore((state) => @@ -286,6 +319,15 @@ function Management() { > + + + Date: Tue, 18 Jun 2024 10:15:34 +0800 Subject: [PATCH 05/24] =?UTF-8?q?feat:=20Mobx-Zustand=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E5=AE=8C=E6=88=90=EF=BC=8C=E5=88=A0=E9=99=A4=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E5=92=8C=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 -- src/components/SearchForm.jsx | 16 +++++++++++++ src/main.jsx | 5 ---- src/stores/Root.js | 39 -------------------------------- src/stores/StoreContext.js | 7 ------ src/views/account/Management.jsx | 35 ++++++++++++++-------------- 6 files changed, 34 insertions(+), 70 deletions(-) delete mode 100644 src/stores/Root.js delete mode 100644 src/stores/StoreContext.js 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/SearchForm.jsx b/src/components/SearchForm.jsx index 0e33c82..46021f9 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -176,6 +176,22 @@ function getFields(props) { , fieldProps?.dates?.col || midCol ), + item( + 'username', + 3, + + + , + fieldProps?.username?.col || 4 + ), + item( + 'realname', + 4, + + + , + fieldProps?.realname?.col || 4 + ), ]; baseChildren = baseChildren diff --git a/src/main.jsx b/src/main.jsx index cc113a9..565edab 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"; @@ -81,15 +79,12 @@ const router = createBrowserRouter([ } ]); -const rootStore = new RootStore(); ReactDOM.createRoot(document.getElementById("root")).render( // -
Loading...
} /> -
//
); 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/views/account/Management.jsx b/src/views/account/Management.jsx index 4266fa3..42855ee 100644 --- a/src/views/account/Management.jsx +++ b/src/views/account/Management.jsx @@ -283,7 +283,7 @@ function Management() { open={isAccountModalOpen} onOk={handleAccountOk} onCancel={handleAccountCancel} >
{ - setDataLoading(true) - fetchReservationList(formVal) - .catch(ex => { - notification.error({ - message: 'Notification', - description: ex.message, - placement: 'top', - duration: 4, - }) - }) - .finally(() => { - setDataLoading(false) - }) + onSubmit={(err, formValues, filedsVal) => { + console.info(formValues) + // setDataLoading(true) + // fetchReservationList(formVal) + // .catch(ex => { + // notification.error({ + // message: 'Notification', + // description: ex.message, + // placement: 'top', + // duration: 4, + // }) + // }) + // .finally(() => { + // setDataLoading(false) + // }) }} /> From 74a68d705c968d3f3ff53a7c283d8da7c9d2b78c Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Tue, 18 Jun 2024 10:31:18 +0800 Subject: [PATCH 06/24] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20ThemeContext?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.jsx | 10 ++++++---- src/stores/ThemeContext.js | 7 +++++++ 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 src/stores/ThemeContext.js diff --git a/src/main.jsx b/src/main.jsx index 565edab..d485281 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -82,9 +82,11 @@ const router = createBrowserRouter([ ReactDOM.createRoot(document.getElementById("root")).render( // -
Loading...
} - /> + +
Loading...
} + /> +
//
); 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 From f22c9eb4971dff1d26fde22f8042c48016090974 Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Tue, 18 Jun 2024 10:45:04 +0800 Subject: [PATCH 07/24] =?UTF-8?q?bug:=20=E4=BF=AE=E5=A4=8D=20ThemeContext?= =?UTF-8?q?=20=E5=AF=BC=E5=85=A5=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.jsx | 1 + src/stores/Auth.js | 17 +---------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/main.jsx b/src/main.jsx index d485281..2782a85 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -30,6 +30,7 @@ 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 { PERM_ACCOUNT_MANAGEMENT, PERM_OVERSEA, PERM_AIR_TICKET } from '@/config' diff --git a/src/stores/Auth.js b/src/stores/Auth.js index 59e27cc..fe0900e 100644 --- a/src/stores/Auth.js +++ b/src/stores/Auth.js @@ -154,19 +154,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 From f323f0b511e858f253afec08ab85973e62a16f59 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 18 Jun 2024 13:57:55 +0800 Subject: [PATCH 08/24] =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/common.json | 3 +++ public/locales/en/products.json | 18 +++++++++++++++++- public/locales/zh/common.json | 3 +++ public/locales/zh/products.json | 20 ++++++++++++++++++-- src/components/AuditStateSelector.jsx | 12 ++++++++++++ src/components/SearchForm.jsx | 20 ++++++-------------- src/components/SecondHeaderWrapper.jsx | 2 +- src/hooks/useProductsSets.js | 12 ++++++------ src/views/products/Index.jsx | 10 ++++++---- 9 files changed, 72 insertions(+), 28 deletions(-) create mode 100644 src/components/AuditStateSelector.jsx diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 0810454..09471cf 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -21,6 +21,9 @@ "preview": "Preview", "Total": "Total", "Action": "Action", + "Import": "Import", + "Export": "Export", + "Copy": "Copy", "Login": "Login", "Username": "Username", diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 2076875..637b8f8 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -16,11 +16,27 @@ "Rejected": "Rejected", "Published": "Published" }, + "Status": "Status", + "Title": "Title", "Vendor": "Vendor", "AuState": "Audit State", "CreatedBy": "Created By", "CreateDate": "Create Date", "AuditedBy": "Audited By", - "AuditDate": "Audit Date" + "AuditDate": "Audit Date", + + "Quotation": "Quotation", + "Unit": "Unit", + "GroupSize": "Group Size", + "UseDates": "Use Dates", + "Weekdays": "Weekdays", + + "AgeType": { + "Type": "Age Type", + "Adult": "Adult", + "Child": "Child" + }, + + "#": "#" } diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index 4ef0694..2c54705 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -21,6 +21,9 @@ "preview": "预览", "Total": "总数", "Action": "操作", + "Import": "导入", + "Export": "导出", + "Copy": "复制", "Login": "登录", "Username": "账号", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 326bd78..8d5c4e9 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -16,11 +16,27 @@ "Rejected": "已拒绝", "Published": "已发布" }, + "Status": "状态", + "Title": "名称", "Vendor": "供应商", "AuState": "审核状态", "CreatedBy": "提交人员", "CreateDate": "提交时间", - "Auditors": "审核人员", - "AuditDate": "审核时间" + "AuditedBy": "审核人员", + "AuditDate": "审核时间", + + "Quotation": "报价", + "Unit": "单位", + "GroupSize": "人等", + "UseDates": "使用日期", + "Weekdays": "有效日", + + "AgeType": { + "Type": "人群", + "Adult": "成人", + "Child": "儿童" + }, + + "#": "#" } diff --git a/src/components/AuditStateSelector.jsx b/src/components/AuditStateSelector.jsx new file mode 100644 index 0000000..db4c77d --- /dev/null +++ b/src/components/AuditStateSelector.jsx @@ -0,0 +1,12 @@ +import { Select } from 'antd'; +import { useProductsAuditStates } from '@/hooks/useProductsSets'; + +const AuditStateSelector = ({ ...props }) => { + const states = useProductsAuditStates(); + return ( + <> + + + , fieldProps?.auditState?.col || 3 ), diff --git a/src/components/SecondHeaderWrapper.jsx b/src/components/SecondHeaderWrapper.jsx index 2b2ffca..097b0d4 100644 --- a/src/components/SecondHeaderWrapper.jsx +++ b/src/components/SecondHeaderWrapper.jsx @@ -14,7 +14,7 @@ const HeaderWrapper = ({ children, header, ...props }) => {
{/* {header} */} -
{header}
+
{header}
diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 4545d91..ee33d47 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -27,17 +27,17 @@ export const useProductsTypes = () => { }; -export const useProductsAuditStatus = () => { +export const useProductsAuditStates = () => { const [types, setTypes] = useState([]); const { t, i18n } = useTranslation(); useEffect(() => { const newData = [ - { value: '0', label: 'New' }, - { value: '1', label: 'Pending' }, - { value: '2', label: 'Approve' }, - { value: '3', label: 'Rejected' }, - { value: '4', label: 'Published' }, + { value: '0', label: t('products:auditState.New') }, + { value: '1', label: t('products:auditState.Pending') }, + { value: '2', label: t('products:auditState.Approve') }, + { value: '3', label: t('products:auditState.Rejected') }, + { value: '4', label: t('products:auditState.Published') }, ]; setTypes(newData); }, [i18n.language]); diff --git a/src/views/products/Index.jsx b/src/views/products/Index.jsx index b793b2f..df1b189 100644 --- a/src/views/products/Index.jsx +++ b/src/views/products/Index.jsx @@ -17,6 +17,7 @@ function Index() { useEffect(() => {}, []); const showTotal = (total) => `Total ${total} items`; + const columns = [ { title: t('products:Vendor'), key: 'vendor', dataIndex: 'travel_agency_name' }, { title: t('products:CreatedBy'), key: 'created_by', dataIndex: 'created_by' }, @@ -38,9 +39,6 @@ function Index() { return ( { }} /> - +
From 2cb059cc066ff28e06ec1dc2050e99e86fff0f29 Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Tue, 18 Jun 2024 16:57:26 +0800 Subject: [PATCH 09/24] =?UTF-8?q?feat:=20postForm=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E9=94=99=E8=AF=AF=E6=A0=A1=E9=AA=8C=EF=BC=8C?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E5=B8=90=E5=8F=B7=EF=BC=8C=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E3=80=81=E7=BC=96=E8=BE=91=E8=B4=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/Account.js | 108 ++++++++++++ src/utils/request.js | 1 + src/views/account/Management.jsx | 290 +++++++++++++++---------------- 3 files changed, 247 insertions(+), 152 deletions(-) create mode 100644 src/stores/Account.js diff --git a/src/stores/Account.js b/src/stores/Account.js new file mode 100644 index 0000000..6ab625f --- /dev/null +++ b/src/stores/Account.js @@ -0,0 +1,108 @@ +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 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 fetchRoleList = async () => { + + const { errcode, result } = await fetchJSON( + `${HT_HOST}/service-CooperateSOA/get_role_list`) + return errcode !== 0 ? {} : result +} + +const useAccountStore = create((set, get) => ({ + + accountList: [], + + selectedAccount: null, + + selectAccount: (account) => { + set(() => ({ + selectedAccount: account + })) + }, + + disableAccount: async (accountId) => { + + const formData = new FormData() + formData.append('wu_id', accountId) + formData.append('account_status', 'enable') + + const result = await postAccountStatus(formData) + + console.info(result) + }, + + saveOrUpdateAccount: async (formValues) => { + const { selectedAccount } = get() + const { userId } = usingStorage() + const formData = new FormData() + formData.append('wu_id', selectedAccount.userId) + formData.append('lmi_sn', selectedAccount.lmi_sn) + formData.append('lmi2_sn', selectedAccount.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, + roleId: r.roles, + role: r.roles_name, + } + }) + + set(() => ({ + accountList: mapAccoutList + })) + }, +})) + +export default useAccountStore \ No newline at end of file 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/account/Management.jsx b/src/views/account/Management.jsx index 42855ee..e05eaa1 100644 --- a/src/views/account/Management.jsx +++ b/src/views/account/Management.jsx @@ -4,7 +4,8 @@ import { ExclamationCircleFilled } from '@ant-design/icons' import { useTranslation } from 'react-i18next' import useFormStore from '@/stores/Form' import useAuthStore from '@/stores/Auth' -import useReservationStore from '@/stores/Reservation' +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' @@ -134,9 +135,9 @@ function Management() { }, ] - function accountRender(text) { + function accountRender(text, account) { return ( - + ) } @@ -146,11 +147,11 @@ function Management() { ) } - function actionRender() { + function actionRender(text, account) { return ( - - + + ) } @@ -160,68 +161,36 @@ function Management() { setPermissionValue(newValue) } + const onAccountSeleted = async (account) => { + selectAccount(account) + console.info(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 [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: '怡小芳', - travelAgency: '三千界', - email: 'xiaofang@yiran.com', - role: '国内供应商', - lastLogin: '2024-06-12 13:53' - }, - { - key: 2, - username: 'int-robin', - realname: 'Robin', - travelAgency: 'IAT', - email: 'robin@int.com', - role: '海外供应商', - lastLogin: '2024-06-12 13:53' - }, - { - key: 3, - username: 'betty-wu', - realname: '吴雪', - travelAgency: '桂林国旅', - email: 'betty@hainatravel.com', - role: '客服组', - lastLogin: '2024-06-12 13:53' - }, - { - key: 4, - username: 'lancy', - realname: '吴金倩', - travelAgency: '海纳国旅', - email: 'lancy@hainatravel.com', - role: '产品组', - lastLogin: '2024-06-12 13:53' - }, - { - key: 5, - username: 'LYJ', - realname: '廖一军', - travelAgency: '海纳国际旅行社', - 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 [searchAccountByCriteria, accountList, disableAccount, selectedAccount, saveOrUpdateAccount, selectAccount] = + useAccountStore((state) => + [state.searchAccountByCriteria, state.accountList, state.disableAccount, state.selectedAccount, state.saveOrUpdateAccount, state.selectAccount]) const { notification, modal } = App.useApp() const handleAccountOk = () => { + console.info('handleAccountOk') + console.info(editAccountForm) } const handleAccountCancel = () => { @@ -229,49 +198,54 @@ function Management() { } const handleRoleOk = () => { + console.info('handleRoleOk') } const handleRoleCancel = () => { setRoleModalOpen(false) } - const onFinish = (values) => { + const onAccountFinish = (values) => { console.log(values) + saveOrUpdateAccount(values) + .catch(ex => { + console.info(ex.message) + notification.error({ + message: 'Notification', + description: ex.message, + placement: 'top', + duration: 4, + }) + }) } - const onFinishFailed = (error) => { + const onAccountFinishFailed = (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) => { 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') + console.log('ResetPassword') }, onCancel() { - console.log('Cancel') }, }) } @@ -280,77 +254,92 @@ function Management() { <> - ( + - {t('account:management.newAccount')} - - - - - - - - - - - - - - - - + {dom} + + )} + > + + + + + + + + + + + + + + + {/* Role Edit */} {t('account:management.newRole')} @@ -407,10 +396,6 @@ function Management() { {t('account:management.tile')} { console.info(formValues) - // setDataLoading(true) - // fetchReservationList(formVal) - // .catch(ex => { - // notification.error({ - // message: 'Notification', - // description: ex.message, - // placement: 'top', - // duration: 4, - // }) - // }) - // .finally(() => { - // setDataLoading(false) - // }) + setDataLoading(true) + searchAccountByCriteria(formValues) + .catch(ex => { + notification.error({ + message: 'Notification', + description: ex.message, + placement: 'top', + duration: 4, + }) + }) + .finally(() => { + setDataLoading(false) + }) }} /> @@ -449,6 +434,7 @@ function Management() {
Date: Wed, 19 Jun 2024 10:19:43 +0800 Subject: [PATCH 10/24] =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2:=20=E8=A1=A8=E6=A0=BC,=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/zh/products.json | 2 +- src/hooks/useProductsSets.js | 10 +- src/stores/Products/Index.js | 246 +++++++++++++++++++++++++++++++- src/views/App.jsx | 1 - src/views/products/Audit.jsx | 106 ++++++++++++-- src/views/products/Index.jsx | 6 +- 6 files changed, 351 insertions(+), 20 deletions(-) diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 8d5c4e9..932fc24 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -30,7 +30,7 @@ "Unit": "单位", "GroupSize": "人等", "UseDates": "使用日期", - "Weekdays": "有效日", + "Weekdays": "有效日/周X", "AgeType": { "Type": "人群", diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index ee33d47..888368d 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -33,11 +33,11 @@ export const useProductsAuditStates = () => { useEffect(() => { const newData = [ - { value: '0', label: t('products:auditState.New') }, - { value: '1', label: t('products:auditState.Pending') }, - { value: '2', label: t('products:auditState.Approve') }, - { value: '3', label: t('products:auditState.Rejected') }, - { value: '4', label: t('products:auditState.Published') }, + { key: '0', value: '0', label: t('products:auditState.New') }, + { key: '1', value: '1', label: t('products:auditState.Pending') }, + { key: '2', value: '2', label: t('products:auditState.Approve') }, + { key: '3', value: '3', label: t('products:auditState.Rejected') }, + { key: '4', value: '4', label: t('products:auditState.Published') }, ]; setTypes(newData); }, [i18n.language]); diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 872bec9..1747757 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -3,10 +3,19 @@ import { devtools } from 'zustand/middleware'; import { fetchJSON } from '@/utils/request'; import { HT_HOST } from '@/config'; +import { groupBy } from '@/utils/commons'; + +const searchAgency = async (param) => { + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param); + +}; +const getAgencyProducts = async (param) => { + const { errcode, Result, Result1 } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param); +}; const initialState = { loading: false, - productsList: [ + agencyList: [ { 'audit_date': '2001-03-03', 'travel_agency_name': '新油低外', @@ -18,6 +27,237 @@ const initialState = { 'audit_state': '1', }, ], + activeAgency: {}, + agencyProducts: groupBy([ + { + "info": { + "id": "640000198509289851", + "title": "如拉下完公", + "code": "grlkt", + "type": "Guide", + "audit_state": "1", + "create_date": "2022-01-13", + "created_by": "郝涛", + "travel_agency_id": "710000200712195349", + "travel_agency_name": "国得气验", + "lastedit_memo": "划百引程级门会需代领主属快。", + "remarks": "及决对金利低集小理电和按常如门。", + "duration": 2, + "duration_unit": "m", + "open_weekdays": "6", + "recommends_rate": 3, + "dept": 1, + "display_to_c": "2", + "km": 27, + "city_id": 77, + "city_name": "称命" + }, + "quotation": [ + { + "id": "21000020030611324X", + "value": 70, + "currency": "CNY", + "unit": "团", + "age_type": "儿童", + "group_size_min": 4, + "group_size_max": 4, + "use_dates_start": "2004-01-19", + "use_dates_end": "1990-03-10", + "weekdays": "4", + "audit_state": "ea pariatur", + "lastedit_memo": "sunt" + }, + { + "id": "610000197306240177", + "value": 86, + "currency": "CNY", + "unit": "人", + "age_type": "儿童", + "group_size_min": 6, + "group_size_max": 8, + "use_dates_start": "1996-12-16", + "use_dates_end": "1974-11-19", + "weekdays": "4", + "audit_state": "aliqua aute quis ipsum", + "lastedit_memo": "commodo adipisicing ea ipsum" + } + ], + "lgc_details": [ + { + "lgc": "mollit", + "title": "林运但", + "description": "学克信图走法因心委周说步将且文手越。", + "id": "35" + }, + { + "lgc": "et laborum", + "title": "备上引深量知量", + "description": "到听少文话包由北层中争二调原务越明在。", + "id": "74" + }, + { + "lgc": "minim velit", + "title": "安都始新", + "description": "取影压前手府要青白支大而。", + "id": "23" + } + ] + }, + { + "info": { + "id": "41000019901227754X", + "title": "据划京少国取", + "code": "ore", + "type": "Guide", + "audit_state": "2", + "create_date": "1979-01-31", + "created_by": "陆芳", + "travel_agency_id": "110000198612200137", + "travel_agency_name": "少平酸型", + "lastedit_memo": "八想军也装运知长示各院步济水。", + "remarks": "千改原统实专回列参目党却是样与后收。", + "duration": 3, + "duration_unit": "d", + "open_weekdays": "5", + "recommends_rate": 5, + "dept": 1, + "display_to_c": "2", + "km": 30, + "city_id": 62, + "city_name": "业入" + }, + "quotation": [ + { + "id": "37000019760525515X", + "value": 93, + "currency": "CNY", + "unit": "团", + "age_type": "成人", + "group_size_min": 7, + "group_size_max": 11, + "use_dates_start": "1992-11-22", + "use_dates_end": "1997-07-16", + "weekdays": "7", + "audit_state": "id nulla irure cupidatat", + "lastedit_memo": "quis aute reprehenderit consectetur" + }, + { + "id": "150000199506023175", + "value": 90, + "currency": "CNY", + "unit": "人", + "age_type": "儿童", + "group_size_min": 9, + "group_size_max": 10, + "use_dates_start": "2007-09-11", + "use_dates_end": "2013-07-27", + "weekdays": "5", + "audit_state": "commodo ad ut", + "lastedit_memo": "id anim incididunt" + } + ], + "lgc_details": [ + { + "lgc": "adipisicing elit Excepteur in", + "title": "很很结龙认", + "description": "事起复京长立然将采共层列工。", + "id": "43" + }, + { + "lgc": "dolore fugiat", + "title": "专中小", + "description": "示史想当集认点离反而原化精满并计前。", + "id": "28" + }, + { + "lgc": "sunt consectetur ea cillum", + "title": "他率带没", + "description": "节经厂面际是统表王活基书色活至是干验。", + "id": "83" + }, + { + "lgc": "incididunt labore fugiat", + "title": "精话西改", + "description": "须事金性别民学少拉个且须专需断连。", + "id": "97" + }, + { + "lgc": "dolore id", + "title": "文技话", + "description": "上任成条到则查支外很素给务府三。", + "id": "99" + } + ] + }, + { + "info": { + "id": "44000019990112280X", + "title": "节到和", + "code": "ixlmndtmz", + "type": "Meals", + "audit_state": "1", + "create_date": "2006-12-30", + "created_by": "易敏", + "travel_agency_id": "640000197111288408", + "travel_agency_name": "术备带走", + "lastedit_memo": "认队什教调问传改万消然声地全。", + "remarks": "属须厂几问总识看部群该克员方。", + "duration": 2, + "duration_unit": "m", + "open_weekdays": "6", + "recommends_rate": 3, + "dept": 2, + "display_to_c": "1", + "km": 13, + "city_id": 55, + "city_name": "铁以" + }, + "quotation": [ + { + "id": "13000019860219219X", + "value": 88, + "currency": "CNY", + "unit": "团", + "age_type": "儿童", + "group_size_min": 2, + "group_size_max": 4, + "use_dates_start": "1991-03-19", + "use_dates_end": "1974-03-13", + "weekdays": "3", + "audit_state": "officia voluptate ad adipisicing dolore", + "lastedit_memo": "Duis amet veniam enim" + }, + { + "id": "420000201706118123", + "value": 61, + "currency": "CNY", + "unit": "人", + "age_type": "儿童", + "group_size_min": 4, + "group_size_max": 10, + "use_dates_start": "1992-04-23", + "use_dates_end": "1970-07-19", + "weekdays": "5", + "audit_state": "commodo labore", + "lastedit_memo": "ullamco anim culpa do in" + } + ], + "lgc_details": [ + { + "lgc": "ut minim", + "title": "回等这意", + "description": "农满界个整千书得被写况空派会想头无。", + "id": "40" + }, + { + "lgc": "laborum id elit irure commodo", + "title": "增正数白养土子", + "description": "么划才共别程以元于族完难变。", + "id": "84" + } + ] + } +], row => row.info.type), }; export const useProductsStore = create( devtools((set, get) => ({ @@ -25,7 +265,9 @@ export const useProductsStore = create( ...initialState, // state actions - setProductsList: (productsList) => set({ productsList }), + setAgencyList: (agencyList) => set({ agencyList }), + setActiveAgency: activeAgency => set({ activeAgency }), + setAgencyProducts: agencyProducts => set({ agencyProducts }), reset: () => set(initialState), diff --git a/src/views/App.jsx b/src/views/App.jsx index cbb2949..0edd221 100644 --- a/src/views/App.jsx +++ b/src/views/App.jsx @@ -152,7 +152,6 @@ function App() { { label: {t('account:management.tile')}, key: '3' }, { type: 'divider' }, { label: {t('Logout')}, key: '4' }, - { label: {t('ChangeVendor')}, key: 'change-vendor' }, ], { type: 'divider' }, { label: <>v{BUILD_VERSION}, key: 'BUILD_VERSION' }, diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index f4187a2..cc5b79f 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -1,14 +1,22 @@ import { createContext, useContext, useEffect, useState } from 'react'; -import { Button, Table, Tabs } from 'antd'; +import { Link, useLocation, } from 'react-router-dom'; +import { Button, Collapse, Table, Tabs, Typography, Space, } from 'antd'; import { useProductsTypes } from '@/hooks/useProductsSets'; import SecondHeaderWrapper from '@/components/SecondHeaderWrapper'; +import { useTranslation } from 'react-i18next'; +import useProductsStore from '@/stores/Products/Index'; +import { isEmpty } from '@/utils/commons'; -const Header = () => { +const Header = ({title, ...props}) => { + const { t } = useTranslation(); return ( -
-
- - +
+
+

{title}

+
+ {/* */} + {/* */} +
); }; @@ -20,11 +28,93 @@ const TypesTabs = () => { ) } +const subjectComponents = { + // 'sum_profit': ProfitTable, + // 'in_order_count': Count, + // 'confirm_order_count': Count, + // 'depart_order_count': Count, + // 'confirm_rates': Rates, + // 'praise_rates': Rates, + // 'sum_person_num': Count, +}; + +const PriceTable = ({dataSource}) => { + const { t } = useTranslation('products'); + // console.log(dataSource, ); + const columns = [ + { key: 'title', dataIndex: ['info', 'title'], title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }) }, + { key: 'value', title: t('Quotation'), render: (_, { value, currency, unit }) => `${value} ${currency} / ${unit}` }, + // {key: 'price', title: t('Currency'), }, + // {key: 'currency', title: t('Currency'), }, + // {key: 'unit', title: t('Unit'), }, + { key: 'ageType', dataIndex: ['age_type'], title: t('AgeType.Type') }, + { + key: 'groupSize', + dataIndex: ['group_size_min'], + title: t('GroupSize'), + render: (_, { group_size_min, group_size_max} ) => `${group_size_min} - ${group_size_max}`, + }, + { + key: 'useDates', + dataIndex: ['use_dates_start'], + title: t('UseDates'), + render: (_, { use_dates_start, use_dates_end} ) => `${use_dates_start} ~ ${use_dates_end}`, + }, + { key: 'weekdays', dataIndex: ['weekdays'], title: t('Weekdays'), }, + {key: 'status', title: t('Status'), render: () => '已通过'}, + { + title: '', + key: 'action', + render: () => ( + + + + + ), + }, + ]; + return
r.id} />; +} + +/** + * + */ +const TypesPanels = () => { + const [loading, agencyProducts] = useProductsStore((state) => [state.loading, state.agencyProducts]); + // console.log(agencyProducts); + const productsTypes = useProductsTypes(); + const [activeKey, setActiveKey] = useState([]); + const [showTypes, setShowTypes] = useState([]); + useEffect(() => { + // 只显示有产品的类型 + const hasDataTypes = Object.keys(agencyProducts); + const _show = productsTypes + .filter((kk) => hasDataTypes.includes(kk.value)) + .map((ele) => ({ ...ele, children: r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i=== 0 ? c.quotation.length : 0 }))), [])} /> })); + setShowTypes(_show); + + setActiveKey(isEmpty(_show) ? [] : [_show[0].key]); + + return () => {}; + }, [productsTypes, agencyProducts]); + + const onCollapseChange = (_activeKey) => { + setActiveKey(_activeKey) + } + return ( + + ) +} + const Audit = ({ ...props }) => { + // const [loading, agencyProducts] = useProductsStore((state) => [state.loading, state.agencyProducts]); + return ( <> - }> - + }> +
+ + {/* */}
); diff --git a/src/views/products/Index.jsx b/src/views/products/Index.jsx index df1b189..0562519 100644 --- a/src/views/products/Index.jsx +++ b/src/views/products/Index.jsx @@ -1,17 +1,17 @@ import { useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; import { Row, Col, Space, Typography, Table, Button } from 'antd'; -import useProductsStore from '@/stores/Products/Index'; import { usingStorage } from '@/hooks/usingStorage'; import SearchForm from '@/components/SearchForm'; import dayjs from 'dayjs'; import { useTranslation } from 'react-i18next'; +import useProductsStore from '@/stores/Products/Index'; import { useProductsTypes } from '@/hooks/useProductsSets'; function Index() { const { t } = useTranslation(); const { userId } = usingStorage(); - const [loading, productsList] = useProductsStore((state) => [state.loading, state.productsList]); + const [loading, agencyList, ] = useProductsStore((state) => [state.loading, state.agencyList, ]); const [noticeList, setNoticeList] = useState([]); useEffect(() => {}, []); @@ -56,7 +56,7 @@ function Index() { />
-
+
From a579ee4ee5f3686e287408296da17c8d59c39725 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 19 Jun 2024 10:20:21 +0800 Subject: [PATCH 11/24] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=8F=82=E6=95=B0:=20l?= =?UTF-8?q?gc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/i18n/LanguageSwitcher.jsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/i18n/LanguageSwitcher.jsx b/src/i18n/LanguageSwitcher.jsx index ac1f009..af87005 100644 --- a/src/i18n/LanguageSwitcher.jsx +++ b/src/i18n/LanguageSwitcher.jsx @@ -1,6 +1,12 @@ -import React, { useState } from 'react'; -import { Dropdown, Menu } from 'antd'; +import { useState, useEffect } from 'react'; +import { Dropdown } from 'antd'; import { useTranslation } from 'react-i18next'; +import { appendRequestParams } from '@/utils/request'; + +const i18n_to_htcode = { + 'zh': 2, + 'en': 1, +}; /** * 语言选择组件 @@ -8,6 +14,13 @@ import { useTranslation } from 'react-i18next'; const Language = () => { const { t, i18n } = useTranslation(); const [selectedKeys, setSelectedKeys] = useState([i18n.language]); + + useEffect(() => { + appendRequestParams('lgc', i18n_to_htcode[i18n.language]); + + return () => {}; + }, [i18n.language]); + // 切换语言事件 const handleChangeLanguage = ({ key }) => { setSelectedKeys([key]); From d24c34d4acf93dacae1fa35e427a37ade7cda7d3 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 19 Jun 2024 13:45:11 +0800 Subject: [PATCH 12/24] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E5=AE=A2=E6=9C=8D=E9=A6=96=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/common.json | 4 +++ public/locales/en/products.json | 5 +++- public/locales/zh/common.json | 4 +++ public/locales/zh/products.json | 4 ++- src/components/SearchForm.jsx | 8 +++--- src/hooks/useProductsSets.js | 16 ++++++++---- src/stores/Products/Index.js | 12 +++++++-- src/views/products/Audit.jsx | 44 +++++++++++++-------------------- src/views/products/Index.jsx | 35 +++++++++++++++++--------- 9 files changed, 80 insertions(+), 52 deletions(-) diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 09471cf..b113807 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -25,6 +25,10 @@ "Export": "Export", "Copy": "Copy", + "Table": { + "Total": "Total {{total}} items" + }, + "Login": "Login", "Username": "Username", "Password": "Password", diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 637b8f8..35c6d67 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -12,11 +12,12 @@ "auditState": { "New": "New", "Pending": "Pending", - "Approve": "Approve", + "Approved": "Approved", "Rejected": "Rejected", "Published": "Published" }, "Status": "Status", + "State": "State", "Title": "Title", "Vendor": "Vendor", @@ -27,6 +28,8 @@ "AuditDate": "Audit Date", "Quotation": "Quotation", + "Offer": "Offer", + "Unit": "Unit", "GroupSize": "Group Size", "UseDates": "Use Dates", diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index 2c54705..e3239a1 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -25,6 +25,10 @@ "Export": "导出", "Copy": "复制", + "Table": { + "Total": "共 {{total}} 条" + }, + "Login": "登录", "Username": "账号", "Password": "密码", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 932fc24..d4d640d 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -12,11 +12,12 @@ "auditState": { "New": "新增", "Pending": "待审核", - "Approve": "已通过", + "Approved": "已通过", "Rejected": "已拒绝", "Published": "已发布" }, "Status": "状态", + "State": "状态", "Title": "名称", "Vendor": "供应商", @@ -27,6 +28,7 @@ "AuditDate": "审核时间", "Quotation": "报价", + "Offer": "报价", "Unit": "单位", "GroupSize": "人等", "UseDates": "使用日期", diff --git a/src/components/SearchForm.jsx b/src/components/SearchForm.jsx index ab5626b..9f55352 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -50,7 +50,7 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => { { key: 'endtime', transform: (arrVal) => (arrVal ? arrVal[1].format(SMALL_DATETIME_FORMAT) : '') }, ], 'invoiceStatus': { key: 'invoiceStatus', transform: (value) => value?.value || value?.key || '', default: '' }, - 'auditState': { key: 'auditState', transform: (value) => value?.value || value?.key || '', default: '' }, + 'audit_state': { key: 'audit_state', transform: (value) => value?.value || value?.key || '', default: '' }, 'agency': { key: 'agency', transform: (value) => { @@ -217,12 +217,12 @@ function getFields(props) { fieldProps?.agency?.col || 6 ), item( - 'auditState', + 'audit_state', 99, - + , - fieldProps?.auditState?.col || 3 + fieldProps?.audit_state?.col || 3 ), ]; diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 888368d..f002991 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -33,14 +33,20 @@ export const useProductsAuditStates = () => { useEffect(() => { const newData = [ - { key: '0', value: '0', label: t('products:auditState.New') }, - { key: '1', value: '1', label: t('products:auditState.Pending') }, - { key: '2', value: '2', label: t('products:auditState.Approve') }, - { key: '3', value: '3', label: t('products:auditState.Rejected') }, - { key: '4', value: '4', label: t('products:auditState.Published') }, + { key: '-1', value: '-1', label: t('products:auditState.New') }, + { key: '0', value: '0', label: t('products:auditState.Pending') }, + { key: '2', value: '2', label: t('products:auditState.Approved') }, + // { key: '3', value: '3', label: t('products:auditState.Rejected') }, + { key: '1', value: '1', label: t('products:auditState.Published') }, ]; setTypes(newData); }, [i18n.language]); return types; }; + +export const useProductsAuditStatesMapVal = (value) => { + const stateSets = useProductsAuditStates(); + const stateMapVal = stateSets.reduce((r, c) => ({ ...r, [`${c.value}`]: c }), {}); + return stateMapVal; +}; diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 1747757..ecd1fe1 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -5,9 +5,9 @@ import { fetchJSON } from '@/utils/request'; import { HT_HOST } from '@/config'; import { groupBy } from '@/utils/commons'; -const searchAgency = async (param) => { +const searchAgencyAction = async (param) => { const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param); - + return errcode !== 0 ? [] : result; }; const getAgencyProducts = async (param) => { const { errcode, Result, Result1 } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param); @@ -265,6 +265,7 @@ export const useProductsStore = create( ...initialState, // state actions + setLoading: loading => set({ loading }), setAgencyList: (agencyList) => set({ agencyList }), setActiveAgency: activeAgency => set({ activeAgency }), setAgencyProducts: agencyProducts => set({ agencyProducts }), @@ -272,6 +273,13 @@ export const useProductsStore = create( reset: () => set(initialState), // side effects + searchAgency: async (param) => { + const { setLoading, setAgencyList } = get(); + setLoading(true); + const res = await searchAgencyAction(param); + setAgencyList(res); + setLoading(false); + }, })) ); export default useProductsStore; diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index cc5b79f..cbc8fdb 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -1,7 +1,7 @@ import { createContext, useContext, useEffect, useState } from 'react'; import { Link, useLocation, } from 'react-router-dom'; import { Button, Collapse, Table, Tabs, Typography, Space, } from 'antd'; -import { useProductsTypes } from '@/hooks/useProductsSets'; +import { useProductsTypes, useProductsAuditStatesMapVal, useProductsAuditStates } from '@/hooks/useProductsSets'; import SecondHeaderWrapper from '@/components/SecondHeaderWrapper'; import { useTranslation } from 'react-i18next'; import useProductsStore from '@/stores/Products/Index'; @@ -21,26 +21,10 @@ const Header = ({title, ...props}) => { ); }; -const TypesTabs = () => { - const productsTypes = useProductsTypes(); - return ( - - ) -} - -const subjectComponents = { - // 'sum_profit': ProfitTable, - // 'in_order_count': Count, - // 'confirm_order_count': Count, - // 'depart_order_count': Count, - // 'confirm_rates': Rates, - // 'praise_rates': Rates, - // 'sum_person_num': Count, -}; - -const PriceTable = ({dataSource}) => { +const PriceTable = ({dataSource, loading}) => { const { t } = useTranslation('products'); - // console.log(dataSource, ); + const stateMapVal = useProductsAuditStatesMapVal(); + const columns = [ { key: 'title', dataIndex: ['info', 'title'], title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }) }, { key: 'value', title: t('Quotation'), render: (_, { value, currency, unit }) => `${value} ${currency} / ${unit}` }, @@ -52,23 +36,29 @@ const PriceTable = ({dataSource}) => { key: 'groupSize', dataIndex: ['group_size_min'], title: t('GroupSize'), - render: (_, { group_size_min, group_size_max} ) => `${group_size_min} - ${group_size_max}`, + render: (_, { group_size_min, group_size_max }) => `${group_size_min} - ${group_size_max}`, }, { key: 'useDates', dataIndex: ['use_dates_start'], title: t('UseDates'), - render: (_, { use_dates_start, use_dates_end} ) => `${use_dates_start} ~ ${use_dates_end}`, + render: (_, { use_dates_start, use_dates_end }) => `${use_dates_start} ~ ${use_dates_end}`, + }, + { key: 'weekdays', dataIndex: ['weekdays'], title: t('Weekdays') }, + { + key: 'state', + title: t('State'), + render: (_, r) => { + return stateMapVal[`${r.info.audit_state}`]?.label; + }, }, - { key: 'weekdays', dataIndex: ['weekdays'], title: t('Weekdays'), }, - {key: 'status', title: t('Status'), render: () => '已通过'}, { title: '', key: 'action', render: () => ( - - + + ), }, @@ -90,7 +80,7 @@ const TypesPanels = () => { const hasDataTypes = Object.keys(agencyProducts); const _show = productsTypes .filter((kk) => hasDataTypes.includes(kk.value)) - .map((ele) => ({ ...ele, children: r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i=== 0 ? c.quotation.length : 0 }))), [])} /> })); + .map((ele) => ({ ...ele, children: r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i=== 0 ? c.quotation.length : 0 }))), [])} /> })); setShowTypes(_show); setActiveKey(isEmpty(_show) ? [] : [_show[0].key]); diff --git a/src/views/products/Index.jsx b/src/views/products/Index.jsx index 0562519..02d57ec 100644 --- a/src/views/products/Index.jsx +++ b/src/views/products/Index.jsx @@ -1,22 +1,29 @@ -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { Link } from 'react-router-dom'; -import { Row, Col, Space, Typography, Table, Button } from 'antd'; -import { usingStorage } from '@/hooks/usingStorage'; +import { Row, Col, Space, Table } from 'antd'; import SearchForm from '@/components/SearchForm'; import dayjs from 'dayjs'; import { useTranslation } from 'react-i18next'; import useProductsStore from '@/stores/Products/Index'; -import { useProductsTypes } from '@/hooks/useProductsSets'; +import useFormStore from '@/stores/Form'; +import { objectMapper } from '@/utils/commons'; function Index() { const { t } = useTranslation(); - const { userId } = usingStorage(); - const [loading, agencyList, ] = useProductsStore((state) => [state.loading, state.agencyList, ]); + const [loading, agencyList, searchAgency] = useProductsStore((state) => [state.loading, state.agencyList, state.searchAgency]); + const formValuesToSub = useFormStore(state => state.formValuesToSub); - const [noticeList, setNoticeList] = useState([]); - useEffect(() => {}, []); + const handleSearchAgency = (formVal = undefined) => { + const { starttime, endtime, ...param } = formVal || formValuesToSub; + const searchParam = objectMapper(param, { agency: 'travel_agency_id', startdate: 'edit_date1', enddate: 'edit_date2' }); + searchAgency(searchParam); + } - const showTotal = (total) => `Total ${total} items`; + useEffect(() => { + handleSearchAgency(); + }, []); + + const showTotal = (total) => t('Table.Total', { total }); const columns = [ { title: t('products:Vendor'), key: 'vendor', dataIndex: 'travel_agency_name' }, @@ -40,18 +47,22 @@ function Index() { { + handleSearchAgency(formVal); }} /> From 32b097c5b7e54a7afe55b2952ee6b6cc5c90122b Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Wed, 19 Jun 2024 15:38:11 +0800 Subject: [PATCH 13/24] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E8=A7=92?= =?UTF-8?q?=E8=89=B2=E7=AE=A1=E7=90=86=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/价格管理平台.bmpr | Bin 272384 -> 272384 bytes src/main.jsx | 4 +- src/stores/Account.js | 16 ++ src/views/App.jsx | 3 +- src/views/account/Management.jsx | 111 +++--------- src/views/account/RoleList.jsx | 286 +++++++++++++++++++++++++++++++ 6 files changed, 327 insertions(+), 93 deletions(-) create mode 100644 src/views/account/RoleList.jsx diff --git a/doc/价格管理平台.bmpr b/doc/价格管理平台.bmpr index a33839a8211b67d4c152a12ac5c7b00e9d030095..e1065f737e4b4c4b23473125fbab526ac344148e 100644 GIT binary patch delta 30669 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 q3;ic&KKgKx|KyC^z6<>)XNLYg`t73r$=NOD?M{ zsx|!As=A;5y>~zVd+&bnN1ML!r5_t^`o=%`-1mNPe5@L+x~k!-uNtTZtN!X!^{;`d zJ>R->ZJ-*euXR*oDf6kSGgn8dp4{)rx2}92$y0-G{_zL@YD;g`@wdPE!MET1U{mYH z2UTR-@`u-P>6yz=u+|Kr2|<`dud zp)Y*w6Mtv*3%|blX5mLyo_~D1>EfNLYWmQ7drd7r)TpYZYOy*D6(`kJzE6iE~^=lE1_GzLe*7QqNSr4^^|( zS?)~c$*XF$>P_9VDYewd$9}a@&7|0HsyL}0tGnv6)$0@+t1hdKnvmJ*G)y=SLsY(# z3P-Dly20*{FqKzpb(1@XdA>VDFXze&(PL?RG2|YmrM7&#OkJy~{j7S*fHV&C<8_M6 z)$~1t>$|CBGQ|(8^^n$A?WIp$^?018>cyJ4vwZI62|T$BiKl6xBZQ5G_=`N#5w1;C z2LteW1)T|B_`77bUxQ|^)CG}&Rx|(Jo@WkH@-#feZp3ZX-PlSC<674L&ANYM`P3X@ux>Ox=+E7yF!~`6KGs8X7Xlu0 ze>ElVLyDJm$ztA-E_KzD)AO2?pU#hC=P2Rth_b=!DocFKh{cCyudfva0_gC}&THe2z_t*3O zV&0#7JJQ|!uRhrLTv{@dw?e^eJ&SkJ;epV#6Y54Yy50HKANnnm^-yJbjHGXep>whB z(QeJ^zM5%!siP}3tft0?FmNa{11HS&_0+bLL1;^D`+5Ghns21*tGPUsM^@6^@r=`b z3a+QQ_L{XPVL*2ZAEnGtm@}2~7R_K9+Dhq}G_s!WYkA@*jNQtVJkRgxT)B-*=*v?* zxn@Lr>e9m@@HEfdrIyBA3Lm8Dwlsd5xz?WV`)Qy%Tv^Ry*WtwLG_ez&Y}6yupZR>0 zQjZ~qXgEvh$y7I9K^RR9 z0!onw(iY9dKwyQ;`&3{LLNF#Wd!|M%1?!${qSil zucP#MB|RR^JxyGM=-JGc?T~Vr4jQ12GQ=YjA5Bln!DtnUa#K%BLT}Y|TG`^6= zx6;B~xY@W*WgRKjoyTU=XnSZ}%MBA`I6dkPm6+Y0o}PusrCeP~b@%z4OeJS&WIykj z5VxOFgSm5>DL0Wa+cj}S>STCDwk>Bu(g^0uSbm>2QuHbp9zxM#sC$*q*;^QJJg#e%`$VYF%<$P zBM*LMJ_PUO&T;D6$=$ZpI z{xsFC)avn!;RKdz7|d`g`nw_*w3BQ4A|cL^Mz2j3y->UXFM$KOQ#TLfZFo8k=o`{ z{#D)HZt811WmLSTbHkK4ubDF+`nJ;hsSL$%$XN&x%c+KD>JCwJ`8JYo{b^_}--h$# zWcp*B&Zf*j?jPp&bsB9;z0=|2e(s*6rsX_gxQ|ofvyBuuOiylesXrH=>Q{drY)`S7 zjPh;z!E~F;wc`xaWnR;%W;NGYR;J-#&7rB3W(l9?*Hxz2MZS~eY%(H(S|3O&GkIH+ ztN9iAgZY$E8K3t2X1)xhVys&T6US2RTpB?HiL;X4ZHA@o>Gwb?!fI2PVR)NoC-MwM z!jc%SnLSez)S5elKArWL_Ij#^MtZm~(5Ng)* zsBe?0I>Qu3+=s6nDapb+%2Vv}_53mq*=!eSbSZ4uO4n3=8e#|Y%uxRBh3NKtZ%w(y zaKFf>$6Rj|o2*Ddh6tC2tg&PbEb>%HiT32-@_REx(0T-7nc$}WN(eZq`A%qzWehgc z{B#;V&lnxolVBnw9A>OK(#gGQyOEzn(N21PnCcHR;Df1XGF7ojw?oWO-mIVD^ldU# zvJ+mXneAM)!iLh zyC>tmSQD_BM+v$j0w&Tc&Q^cc=x%1kN`7r+rj6w9X|A(v4b)PqGzXv3KV%}5?{k@k z_%Wxsdz|m>89ZJ=h-;*wl{8MjFqg@Pjl9oNn6945Rl0FI*D#cEVw$by?noMXs3+r0 zu2_d#b=7M%_ZHKXNm(W?pRn-I^HX&_2dVL6ZS~~fAQ9(0x2{x5kB7SF>ECD`eE!Aj z<_9MCXPSQLdGM<(n=LC<$Mfg9oBvWK&rax`%U~{rE&PnVwAY_`IhKA+)!d&-PsYNq z-B3RsD)vMBT6$r=T0V5^M!qlS%4I&UQ|EBX)AOq}vsP1LEB8-Q?|J^38{7Gv$+MFm z8>xON+(_1SGRfM)=$(}AO{)zYnRewaY0wZ38=oiADY=4 zy^!x&*Ns%dK4%!urwR(}m+QVPgu*Z=6l;c;;yWvGFT~OPlleWEZ(jEyt~0&nCK%iO zkiQY4c=gvI-_+&stq-S|MB?@kGN0M23J-G|`Kw83LTTx;sy55Z@g$D1^BQ;$xWU{hfgYw{uU zllEsZ@8r?FeBS0td-^#W0vA$-&o^HW%tLsf`rXVS>QX7Crs3o08Y5 z%_3aPi@CcOhHr)BqY!LP(S+?OG1JITv)gMp^mnF$Vj04=ha>> zu*LLaDLfbrCyw$2k7GC;;(QIJBv5fD{A;ETjhusSv{S6vFn?=j(8Xz~R8)p_vWII|+Yi2rg+y zJ*+S)YR(E_%HE_zj0aS3-d02TOfGx94)w#ieH(_B#F|U6ZcU?m zdGUhg!n?kd<7rx`ud_P(Lns@5JJ+si;@b1%a8^%oS|AIHd44@znydO7`Ej1die~44 zTicdmXI}lOgaOeNa*(x=hqpo&U+Os3ujID@v~crmNj(@JM* zHo;d?^0+3hv7f?+VHJyJC{;b>7vr}r{keO;S%~FAuC#@q_S|I$vVIt65Rz9ZznIcw z{b=r!CM1MDbKBtgaDRCGL%73V15Rc-Ho{)Ie>M}!+@H@w;GT(^x`|NCtbid?P)e=k zvC~uuKwL|+tLfQO&4Z1amptnJRIVFT^GWXYWdO!f8XUP?&o-8(-wn_w5gEaVy|m9MuEUfgHE$aw*evw8pla5y}ajt!;fTwU4} zT%J$;KTDOH`6LA|Qw?W=5z!qA=OYKcoL=*f&6Bb8w=ctCwP>2jew^>*AkHl0K9iOc z)|#i(^(xm0M)Ua~t`n$Y4Lm^jvJr9@{#edcB*)-A_GUz_b#sa)f zNe0GDDuYlHGA!n$yarSLB82c<%&oOFSsb*RJb9VcfFZ%b8~Fi97|&;6D^r3hrM=Bn z3WvRGek|1skDfS6&#iILVOuJi4!>qnk`vMy!Y*@nELX_-{xsj0QavFLF$GDQ$vnvV zUrrIW>PhI^O|?do$Y4YQyqCkU*^pGA3~YUsH}??S(?hw>6uk}^@KqsHcsC1TBKL>W z)KJZxnS2*IfbQRi2aBl#SIZD4c!G6cj-^b%>2N@xho|#8eddx;%(r2inGCHVQiehX zPz9XA(q*7g$?T);G`pUf2(iU_K)^`m(_vb9m2w05>swY0@Tflx-{f+49$3qbl{`lE z0ErN{nET6-1HYQ@Ai1tI08$s}=}mJJnJ<7lDoK>0J1kz$6V#69dDNq64z{tC`qgHT z7-0=ITW_jsPcs~GmsL=YPOjajui_P58O!zb_*EVz-4^oPaXxqQVw6xg^B!%6+R3x`(ot%MuIv5?1L>#dtE&P^@<0oDVluD5Q6olo6fh0RzumFnB_ zD0~k=2e}3adQ6k_Uw29zq#-@01qIf@7q3Dh;oKH-ON^Im#t&jqY~~&g_f-4*zx`Yq z4C6S*yk6R#)x(Hp9X+Lr^$-LDW-Tmc%^+?vJXlJ}i98|xQwGU26CvL3ooiu^C zt`NZ?-%2k**{7)ycykfr7jkVTrCX~}_Rn;RQ;K_O=qkVF(hLFFldJ3+!Bjys0T(M< z?1Zl~k{%P*Dx?WS`U)=Os_6;AH1o*@tI489++yyY=egmkK@V{mdh$sH7suitm6-!Y zfO3{*Qx%!#D*uQZeU={j)*6y8(l=s?1|`TY(&SNymQ!+-n#oE|>TQUb$*U(kjEZZd z8e*eApWEpz>h@C+Q=DpkmFlhA#axB$9YzlPTBhG(SUQyJ^Z5%yf1S!jzprzhZsdB( zabQ;#XPrIGDPt4zhaXdOcdK5#0{Ed(+H$e)r}I3#heb^+h;TCf;rCk$nPH z;&4S{-sjo9TxVu%h47Q`>mrQ+wPsUVt^6Li_F4$pNlO=k=QK4@+m@c3rLY-Y;6ZXq zR>FO*ccv_4-S*6zzv>dzyu!Zy$^;DkD6>jR15pYWWO%<0pVxecoy z(+7?eThD|Oixk(ewxJ*eeAjO#w*g*F>AAeU?AiMjKBDafOyJkEM@O=hnxxi0fic>>Fp0~ zlj$9KMP+anpRhYE zGWcLg3EnAo9@=FTV33$_d;LjV08-eul#tc?I*&0jSa-nX{#1o?6u4B8@&Z$|hHFQ; z4s_v3ucyE5=}ZYc@wRFs?XTA?#S#jHAK06|?B#oL6E-ugSo)NdF$O9~XF7@;`1R+% zvETBs#%UftOU)&DWjcKk59~{~i%!1?&nH7azwI{P_UaNWdg%-dAu0miy^#hW9TJvI z4$5{a&7Y;*ObDCKaNOjIlo+uYu2nR+k%7nEhcO%+4zK(u^KvP_xq@{pqHYk22!M_> z1B2+!vpbp1Pnk}p%w8(p$~}tdA>DdNP4lSY!rRCOO-5BlM8MSc%W#xQCGRc&ws3m zTMIV`SFpiuKF>myG{5AOji=haRKA&aZwe51o8bpk+q&g0tmJV(8Iz(tFV@#W9_MIC z@{lq_d~DSvEga!hSr44f<&e$FCzfe-F$1Y{=6oG~78QLAU8fNYuhR%uav%@1rWaxt z#%ee8@=?$0JFGd+T%^x;x%)c28p%`bsiWkavDZN+qZ_uOg z8nl8+1?R9)0plumLy>Ty+S6k%5ezwYhQt$xxhJfLGOXQs z3f3*JMQHzo1+$k&uS0;~knq`U9_4Q`54^zLj3I->o8uam6|tW_U*=gThWR2|&3fAl zALwTTL?Df3z5hn|XlgHKkPh?Nm*J>$D%l-3DZ-ZB4}~QX&Gj1#&2kR)SHA^Ul)TF4 zRT?QTa!fc}x(dIYR0F)=US|dg@3unbP)%G{zSH^wPlMG~Bh=8S)Z}=+UFM3rZ zx52an$>zI?YjVPad`iENSY#T#c;6-8>?*~0o|E}p%tIwq)RvyzhEub7N-*p?pRyxH z(f}9PWE%)!Wyso7duz(Kra1zgtR*9f`OegGM*czK1>@n-Q~4_^1N2jp`MCK-5K|yD zJJ#){(awCTY9NCF4(A67#Z9M{^V}iUFY?<8<1EqN$0;MhfUJcF?uy`m{aEOX1ui64 z@hYRnvH>K|d&q-xPfaXI783Xw+%Hhq=q8B%>I&U_Nh1FeF@NQaW=B;IZHFb>%L0!^3=$K~Av zjS9?iUlXyE5?$fcSR>6c9xzZ6#8LhVkRgH`G`(nT{u%2`=2n4%`S};QwwgP4DG8+& zC|n9>pcoK9cs;~ll&miuIf)$jYQA$s7W2$%6$DoZfSmz{GqxOIQ5M)d*HElN5~n4} z>N1-pKagiuIza;elTl%oLo^sw&XkrCvL7;Ov%03cvH zzYGE=Lhu9jg1D#5eBp90P{DclF&a`jvxd6zgyF<)=CmuF;jaWdbBPvMON|F10!cjj zWH1PO9OuD`yr)p198Yq$UnNA^Gzn&w(Q@Q!l%S z8#x$R0f2$z9p!Cl#wc&(t3|~~An#?%P?ROlk|1f%Z&vMI8lfwABVGoEBO!RcoF_FR zV78SSgbBb!1xlK%i7P>7Rxijpx&MGL`p|~&!wihJSDAZD3!B(jlbv20t5UqAFQmAw376@B8$h;6!??jwVzLN zZ7gP;~5$N)K-RZ_m z-4IO%;*@bCw@to0wyxEzldWbIL*t>h;#btGWU|q4M7VG>gaBQIl_aX2hXhbF`-ej& zIJJ?I_{fWuDUy(6Bb32c#&c5w8jA*yD9LL#qYBNL4slGVGU*tZ61eYEffWJQ7RfYw z7Bhr=*4zBvOwCe$LXs=QxHN~xh1*4Zw3ln z5|UZQ@g<+A;PfTNue_2>+L!XU>Gw-Hj+$5I1q6>>^Jgc@WzAT<69DBjkrKE0JCZw~ zW;tXuKU39u?#ldmkqdB(mk2d~fNu;y?96phP{N7;gBz#+8>vKydmvS}<|5It9p>>& zr2}xTXEQLw4553QDhjkpPI5(r$|v%i^|6y?%xZ^cr)KY-B!LK&$z7Q_o<~*?b5UR3Q6%Lx@yWYaZv;(;50e$cZc* z2={CaU^UYmn<>%7{uR{LZx-KS-6+bp^G(vJK2s&qi6xQ{J3E=x;+9N`5)L=NJz*KH zhRNNjO+;rtC7~mvJ+-Bag}DBjH#dzGI!i@cp;4~9eimffGU`l&_8kbK$vD*DWyqNe z$uDvE`;bD`VKP5Zk})*vJWomR^IUIgVqd9mExpdZiwq%ztWLi*LVEs^cO}~bgl;%s z6GV&X3th*oK53VzWgG^3jIEc6FPGiJq?E7eQ9W`L1uCGAt*b6?&pAysc=I&)r`B=WxSSMpVF)(|6a zJ0;eu>?!+ND)#ElO);qXnj3{lpp}jgh#Ot0f_X;e_+)+xH7utx_$SoMyZ}ZBn!znN zsKl2s7w?pWbDOq6k>UkcjZ_Z`6+$G4hVuxhp*7c~sdB6?Q+PjvV|qyt0jP;(!zM*P zmO~&F1B?=Yh6xfjiy;*ZE%(n#m=1{?c)o`FI2il4u+nE_$~V0!L(#Bn2E&_de7 zROPSm^7_N%SM@9?teZ(6?ot9oj&3_lSpCAlVi%H)sG^CGewe-%Va#%7tP)3)sZlH+ zpuG5&pS&+ID@!{d}m5s<*!_L zYe2~1GNom!h++WZ_j9iZb72=7+aj*$|Jz&!6u~G#FJ%p!=NiCsA-@;v5f_7LOSQb( zk}g4l8TGC-DJfqlg6Y{EG7DA@a*)Wrkv0V61)C;P#t?0#TtgIUB2B$``5Y??a6COA zY{@?vU3@@34_MW7Dpn2U1(sp19EUUx33T={<$A(Rp=D2Y*4;eFwefI7_BPJNJa2^V z>0Gjvg%A}0oKFvh#x$;<((IWMAV%s03~D#dK%N8-cBjNm2909b%eUV2jnrWK7n5s0 zWC_o9rv(^^;4}?z&;Fecb(b*+elxV)q-RnoO-U3&21_n3VY^!P0Cfhu8D(Yh-}DKSx%KAx3p(Znmh;rRMC9OFx^N*@=}bS#bveB84Qv^7?gK; zSY&#vt`vI()_FBthvQVSpMEhM&Em~m7x$KnOiLJwqOKWjWo&uFPnj1&5;hD%e8q zT`!0c##wRK306`M!fDYO=gDwNt_lr6gj;gVk@%~l?p4{hT!$9PJ*q0#?C4+A*-!)5InMM34+bJwlW~&6V>yU$l*ni^J zOl&B%xEnw4SMks{bfs-FY1a-8Oe#kU50J2mW9wl-`9rdId z1W1($K(q!(*JdpSSK9wFfnjT3fwRwQ(2E;^>rYy*9GMr+FbK*H1NM)2W zZY3DYY!i+XNfOI!knkkEh>TC+6k6RT`Qk`I!r*r%qSaB5v7uCFPW*W<>E)Vv|J{-j zEMCfEd+BJN<^Pw_Kzr%Ii`%7&GSQ&>CC#22wp91N*Z@#(cv(l-IhglLdi8AjYNjVi zJoUcpeP|CC3lhNL=SesCbbyihEE7JR?H*i3hEsR$(Ds8FRi2QmsXg<4#i3YW} z%D6F7EIU!|r955YnfBSig5^Q*?3U|wKbPLJgc#dALV;t;6mhbn&Z`E`pXTq_Q-rXGV4ryzS}tOr9~BY{KO2$!L;%%H8ZrO#p3kU7Da65cgBYC;1_g0=AK! zn;jfhF9W(Wx9r6!s5iRBL^Fs|Ddld!{>$kCv4UWkj`l2=hVo3!cKdP)n#I;H3FBNAD%msOb?&M( zoDvr)NC075woRFQI0p&f2^A!u%%0f)TuY9j<4pSVSKjqw8n^qBBVwuarOyQk03UNJ z1?2>z+S4?aLVk*hwZ{R1GMmO^!%0wG$(w+~Eyseu!gkWVv+6Hnc~2^3>Gp-mw4c;h zc{T|jzxVm(cfRwo>96a#<7ZlI`jbt?ATr$lzc&4Cq)7m&?1YodV*wUgo*TiXlrUP` zz;>)Zr8Aa1Eqs*A9Ajq0L5k5Xg1&OGpcvO-C?M6{{d(%t*MD}?pNOX18YS56_L9my zW5Wrq9E5c;(S%>)GEb*!G1@u_KNrCvGB1sh{oG@@0=1N3=4cveK(bRMOHG7W`*4i- zLV5=@7Gm1W%ev(2y-GdoT=@+WYY-!w|2`yS)5ll_KvDn?%7n(W$uz?bzs#VT0AQ8# zTywANnV>TCVIV`tRGv-M$92Qxz6JQ=zm;7L_8OW#43quTggD&_WGb=%t78~~3WdIG zNO{N$uqT~FM5B;&k?+vf0{#?1c$N8w*<5A7cp+Gf4uk>`5aVOz1K)GwX@-15%bmeZD|54U{0>V5O2 z-~Ys$&wTO+-hTSC&A;2y_;y;8w^Yts=}q0XA+Y`)@}2lnH!qa@At8*NTuS(H$*Q+I z?kaZ}i3}V4BmpS1w$L{aPT06+mV$}}NhyT%oq*#V3+f16lw8lJRJE51G>470?(;ZV z54OtN>orjiz0Ey*O**tM{I8<>@odCV&m0G(JTm130QZf zA0`}liguw0Bw|o><^)Tr1hnWfdP}rfHXTUFrfxoJK~U{8F0MST6y0Ugl`!9KNXYp% zMJiX-AEf%hG-6!9(Mw?|fzNU>0H6+z2~+SkO|v5`I{9;s`mm}wFj66U^1LwURK82* za4?>f4v7z@^JVzy7>Y!sobTk2w_=QQ?NMkfWu4g@KuMCpWoznTO7kOaL=uykPsNOW zpa(b6^c3WzC|P9mH6W14qI_;st~VW`XyI-Na2MxR3I?-IV#jQl#bLKHI5u{B+=qad z1m%@fB;5^4GM6T0i^|8@4N?6m%eQ9s88T=kIYHi88$prRgSrv=W$3w6mN+zmmHW8Q;oUIL1^8O|Fpf0H{F52)udRXwJ8PCWCNrsjVyg;>Kwf|{gd zV>=!d)UH2+V&*IWc=%ApM4}Rn>!rsKLGYfKle4^-6s8#k%U;_}Dd1hnjfESSDd2B- zs^4V3rkGD1d0O`AeD0ZT@T)dQ6cC9foh{g9s~tGgj&Lc)%&5myaFtJ%HaH9W*(Pkb zvhKT6hMae0FEhY?z@czqJ0uau@-a=guFL`8F<%{A*4M~aF{TpcH5Gx7!nX35m(sVA zDfbkr9j?H`WTAAW8HbD!+j?e-m64WbN!^g9^aJ4x2Xv08RibxDE+`jGG94u6m7Sq1SLw>^0;cg*9xp+E5h3|y;>1sRSjrB~ zXiJt|a_mx0T-hzwktSpmtfe^y3n0~CmK_u8DQ||=39br&=!Qdc@iX+$Bx|i@+EvX) zQ6jN4318(TF$Z`so{vJX)0ynGV_sOu+&Cbx98;P0d-+DBJ>{Fcd3FM`Irfjd8747- zaT*p%R=5t+3l2cZlx?-*BO*|cM}MJH1+n{` z4#Ma@li<0W{&3*A^m3+nDaPkI*N7pw8pN>d9~cc`JQw3CH3W(zK~MsV$isd}Gvggb z$Y6b8x(%!X1_G%Pz#Qx7~bMP^bK zv9q3%^2>0c#1`$G1QgxRAw@Qbp}H( zLxSDnln*KHTOok4X;Mn>cbrujJ{fqXnfTRwYLMtOlY8Zq3>foui2Y%S=tTp<0qyFv zeyi%=J%4Mx`Qy*;Y&6fj`N57KdHahS&G#)$|Ka&t%grBu^V^#refuLT&HuLf`42vu zLO-zH{Cmk1sQyz;z-Wl0c;O3xafZxDILYsouO=IbIm%ndLweRu6UTnBBZ==Ks%(~_ zb#79MTPk!SX6=9=`_p8GIwT0T)i58(JLnYjNp$ic4cRS$vqrr@p4?CR&x^gpO%>li z%oC#Wkec??vKe+zajZu1OIG+;cxFSCZJ``-UL_nqd#xyIpf!Qzj8l17HZVGV5OF|< zA_-~C{tR>~~?<{`7q| z`Y~lYtgdI0AeALI+PN+v_V%GCFEyQn{e=YMjRJl98S43>8&R#qcfiu zsnRAB!6rk)s$x+a5R%9pei_$D-1kq8#|sZh)f`#cNY8EKTgi)eyO!oK2HfA10#fM8 zE+X-&-IOYBsYD4UY4GiB5R>n2B9oW&3tMbD^%Nasy7uOY<6I%gWUZ1&6V>zA_F6t1 zYnFXtw%Hsg-=Ux}?dd44vdkHQoD6y3$N4M#BA&u_@scTF9@Fp6bE8n6@~j;ga)^U;0Av*AUWIOwUQ>n*U502j6$F^Zn@IMRUqfb!%K+=6tc~# zH@SxmTuOVVIB1r~tNapeIjz|>mu@gTm?*JkXqKT|p!ArEY&vVwE_M0-%q?)1_zBU= zzLSP2$?Y<|d)Ya$mZnZ}-Ql`Rd4_L5=FDce%QQ1J>=gyx7Bo2rPLZ%T=_%t`Y+LH- zV+aw%kyNHLvp}qrthOlup|+|33b`DVlbkVT zBTQ~HcL85?Bupy9YP*6hMhj`^D%D!}0!%ip*!Lm<#V0J?ksGpTplWCrP3ADz7XvgV zYuGeMJIXgJ8tb{?+%Di6Fc5AhEw&C<6G_lDV-1$Kt<=d4qUBh}FTtOQ{9Q=@A5spP zyO}(~wE#jsDj1M9bQ-sS*HG$QN|($kJ4J+R1#j*$07Sq@$V;Xn>=pDOwGiT9qaeV5 zReKwP_^0-M*x?~DSq%7fdQx^s3g@n-Kh}aYHr8HG{*t{QCR4RH*CcK1hcwW?NTU-% zIPKZ4hEQH|+S%uh^poU#D zGQvd=**?q+P6vMvICGxgN!J0?R@MJX<@Vv*>9!vBtfZLeuLSSRQ@Ay95fNoA6n1@&4?lgr@mN44nq~s|?+EneB3Gwt;*L@Uu?H8vnGm*C_+JT z4(BRS88)Jr3j%nejG#p@ zA>+pmKoLcpbCM;&1zus3u;XX~JBQ>I(d_(%o;;IuP5KvBa~dKfnylq_PbyK^G7-#! zzmVa8Wt&j>zDULeZpV0PfP;e{pj<#0&V)f(&NsP4CAra4S0PwJOawSISGK2JQDAga zPL6j00s)H9DHf%W2*0$PR3J2I8>6r!pOx)SNR^asyXx9<1@0kFoZd4vIVju~=*V?C zlI^O+^R^uq>#(yc6$(Fth)Wd8AtB_XU^ClMB!###E9^DmMgtE>I4PUVLMfZVCpNd7 z)pLsbVIweKPB+^cc z(yfOOX!DNsBrC&UpJyngIoPwpRcD28%^i0}Hw~r*2AgcVWM#8oCFhp^?1G%+xi|@? zBrsV$GVfUe@@tujJf*VXZ9W{MJ)xiG$Z~%9=5$Pxn~3HK2p5Vgvu+)JBa_h-PNw}` zn8@?J%fp62T9^G{(lTUFZ-fx384SDIx{tsoCX2)-;UovjLwgLr5I9H^=U`|K*(G_7 z@-zpdH`kZ))JA?=B)5Mp7kFk|11Y?8zF-U3cs~WndI=4ZJRLX7EUv2G{ldq-@a+#g z|NF1BEPd#|z5dYme#E`oxS|s$qba|E$nCy02o(VzI@h~FcA<;USn1p%xvJ$j5Qj*vq(-ONE~E>vd3z(7 zmyh}E5f60s2&2VxbUev)nxN<%jN&D4%lQk!63iK?hFfevGuy9HWrNH2IJqI`yJU@Gy88jEEl*=uFobe;2*b0^;#DV2n z0J$WaI~%$$zo>qmGw9R6?B};(lO@1XFS)_<85puVM+gjuPPrDAt`V9H6Tz8GV6zPT zdzHH!Z}y}RpU44K=cvSvl%hz5vf=T9n^$R^-L@MJJmd-K7IH;IjGa;;N01m!3I&Ad zPiH7$-AnF~g$(d2aDoSbmTkEr&SXkD)I=tya~G(Bt~5rqaz`0WWt*AQK}Y9+enF$M zoJHGeoxGGnqDGhbEA4}YYV!cyDdkfDY$~JYTw%l+cSmP3r0HAoWIcS~CU6+UWk@0p zK6S&Gehle$b@3iSJ)8q=#wIDr;lt?zJ9{?9Y7``CMGU9+ompOD>-WN zzMiMzW+3Uo)Mq-~heV{$rCixvvgEN?#f7CmCA1!12*Nr~4QY zF8fXXN?8MUnd}>xX;Gi`1dui&KICKp$2!>X;S^kRS5}8?s!WBYFrFo2J+($YviKac z2D`YaiL)&STMsf@#j<3|l2A@~T?|p=x21r&AQx-a@q5L=;g(7RbuNnk{iHMVltBYp zd#oBFx*`(}qmi^{*MR>5iDLmMWz82ujSMEYm@G7VSr{x|TQ?YBQaCGR zkU=4ncsJMSvE)|!1>~@BJ|+Fx24rHv>>(+za^||1%^Vkb@8qguK7Woh!*nFINCOu`wR{`S$gq2Y_+f#PsoV_EH-O0*p5-hQ z5r0Q8Pltixz>XvFzi1FK$2Bo_;gkesd6eu$<|Ryo?+q!_9fF8gDOEV*e-Spd=Pz_x z29~ozn1vYbf3lDh33Ded$pks%lG-jp8Z?AGXlAl_ft@$gOZi#iCqk>XFO^}v ztd}FMHNc@Sp0ECvZ3nUm^=vBi+EGat0k(-EX{@4E1hU?6*Lj10OB#V=E84@K!tVXt z#Vvsx8=^$OrZUtt-~PK{r-babRJMw`=jn%*AFfZSbcR7|7$JHN?u1SG-xA1_{XE95 zG3iCt#9w4q0LYx-#9`)h2%Q17A9A0;+Zh@-%Z|wse}!d<_1LsQ58i~uK{y*VpU4^8 z=ZeuGAPOvVMD(8)4zima``I4GjMNR0?7)B#W^q z{Fh!8FjeHuelaq>F{S)zo7R*86*y3Uw31)o%w7>T{tisWRCC&u?Kh&6a(5u$HSYB`)V zpt1jl2R(Pb^2S(^Qn#Euyqap_KBU-isOFjT1uyb| zU2KRm@{(Q1F81(Iep7hD1vVtv-A=HYuw~qdM%X_uOoUC|eD*lY8ngJU)PdY|YMs-> zZ8m0m_>Wz%*ANhpn)Dl&cuIV0Y~4@Q42eP|WuXk_%_hR#Rn(`l-R)75bhaYMB=$oJY zryqUu+1|b9PtP>}!=`~Zzt#4M=dX`8cYgZ&SN{8>KK@+wH>*!pfB3il?*1SBT>tCu zUisAApTF`ymydtt-CrB{`)~izY;${au!*KOANl7!&)*zx{;B6LZs*VM%r$@d?cPN5 zP|JJoywm({b5qmvw?EMIL(l)Zzv}, { path: "account/profile", element: }, { path: "account/management", element: }, + { path: "account/role-list", element: }, { path: "reservation/newest", element: }, { path: "reservation/:reservationId", element: }, { path: "feedback", element: }, diff --git a/src/stores/Account.js b/src/stores/Account.js index 6ab625f..a2391be 100644 --- a/src/stores/Account.js +++ b/src/stores/Account.js @@ -24,6 +24,13 @@ export const postAccountForm = async (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( @@ -54,6 +61,15 @@ const useAccountStore = create((set, get) => ({ console.info(result) }, + 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 { selectedAccount } = get() const { userId } = usingStorage() diff --git a/src/views/App.jsx b/src/views/App.jsx index dd2dec6..779993c 100644 --- a/src/views/App.jsx +++ b/src/views/App.jsx @@ -149,8 +149,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 e05eaa1..f0e7b9f 100644 --- a/src/views/account/Management.jsx +++ b/src/views/account/Management.jsx @@ -161,48 +161,32 @@ function Management() { setPermissionValue(newValue) } - const onAccountSeleted = async (account) => { - selectAccount(account) - console.info(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 [permissionValue, setPermissionValue] = useState(['0-0-0']) const [isAccountModalOpen, setAccountModalOpen] = useState(false) const [isRoleModalOpen, setRoleModalOpen] = useState(false) const [dataLoading, setDataLoading] = useState(false) const [roleAllList, setRoleAllList] = useState([]) - const [editAccountForm, editRoleForm] = Form.useForm() + const [accountForm] = Form.useForm() const [searchAccountByCriteria, accountList, disableAccount, selectedAccount, saveOrUpdateAccount, selectAccount] = useAccountStore((state) => [state.searchAccountByCriteria, state.accountList, state.disableAccount, state.selectedAccount, state.saveOrUpdateAccount, state.selectAccount]) const { notification, modal } = App.useApp() - const handleAccountOk = () => { - console.info('handleAccountOk') - console.info(editAccountForm) - } - - const handleAccountCancel = () => { - setAccountModalOpen(false) - } - - const handleRoleOk = () => { - console.info('handleRoleOk') - } - - const handleRoleCancel = () => { - setRoleModalOpen(false) + const onAccountSeleted = async (account) => { + accountForm.setFieldsValue(account) + selectAccount(account) + console.info(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 onAccountFinish = (values) => { @@ -219,7 +203,7 @@ function Management() { }) } - const onAccountFinishFailed = (error) => { + const onAccountFailed = (error) => { console.log('Failed:', error) // form.resetFields() } @@ -259,20 +243,20 @@ function Management() { htmlType: 'submit', }} title={t('account:management.newAccount')} - open={isAccountModalOpen} onOk={handleAccountOk} onCancel={handleAccountCancel} - destroyOnClose + open={isAccountModalOpen} onOk={() => setAccountModalOpen(false)} onCancel={() => setAccountModalOpen(false)} + destroyOnClose={true} + clearOnDestroy={true} modalRender={(dom) => (
{dom} @@ -341,58 +325,6 @@ function Management() {
- {/* Role Edit */} - - - {t('account:management.newRole')} - - - - - - - - {t('account:management.tile')} - - - diff --git a/src/views/account/RoleList.jsx b/src/views/account/RoleList.jsx new file mode 100644 index 0000000..923d43e --- /dev/null +++ b/src/views/account/RoleList.jsx @@ -0,0 +1,286 @@ +import { useState, useEffect } from 'react' +import { Row, Col, Space, Button, Table, Select, TreeSelect, Typography, Modal, App, Form, Input } from 'antd' +import { ExclamationCircleFilled } from '@ant-design/icons' +import { useTranslation } from 'react-i18next' +import useFormStore from '@/stores/Form' +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: 'oversea-0', + key: 'oversea-0', + children: [ + { + title: '所有海外功能', + value: 'oversea-0-0', + key: 'oversea-0-0', + }, + ], + }, + { + 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: 'B-1-0', + key: 'B-1-0', + }, + { + 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-01', + key: '2-1-01', + }, + { + title: '新增账号', + value: '2-1-11', + key: '2-1-11', + }, + { + title: '禁用账号', + value: '2-1-21', + key: '2-1-21', + }, + { + title: '重置账号密码', + value: '2-1-31', + key: '2-1-31', + }, + { + title: '新增角色', + value: '2-1-41', + key: '2-1-41', + }, + ], + }, +] + +function RoleList() { + const { t } = useTranslation() + + const roleListColumns = [ + { + title: t('account:rolename'), + dataIndex: 'role_name', + render: roleRender + }, + { + title: t('account:createdOn'), + dataIndex: 'created_on', + }, + { + title: t('account:action'), + dataIndex: 'account:action', + render: actionRender + }, + ] + + function roleRender(text, role) { + return ( + + ) + } + + function actionRender(text, account) { + return ( + + + + + ) + } + + const onPermissionChange = (newValue) => { + console.log('onChange ', newValue) + setPermissionValue(newValue) + } + + useEffect (() => { + fetchRoleList() + .then(r => { + setRoleAllList(r) + }) + }, []) + + 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] = + useAccountStore((state) => + [state.saveOrUpdateRole]) + + const { notification, modal } = App.useApp() + + const onRoleSeleted = async (role) => { + roleForm.setFieldsValue(role) + // selectAccount(account) + // console.info(account) + 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 From f8ffaf899d77dd7d106a31a8328da09e3dcf82e3 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 19 Jun 2024 15:55:06 +0800 Subject: [PATCH 14/24] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E5=AE=A2=E6=9C=8D=E5=AE=A1=E6=A0=B8=E4=BA=A7?= =?UTF-8?q?=E5=93=81,=20=E4=BB=B7=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/products.json | 7 +++ public/locales/zh/products.json | 7 +++ src/stores/Products/Index.js | 64 ++++++++++++++++++------ src/views/products/Audit.jsx | 87 +++++++++++++++++++++++++++------ 4 files changed, 134 insertions(+), 31 deletions(-) diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 35c6d67..6033e89 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -16,6 +16,13 @@ "Rejected": "Rejected", "Published": "Published" }, + "auditStateAction": { + "New": "New", + "Pending": "Pending", + "Approved": "Approve", + "Rejected": "Rejecte", + "Published": "Publish" + }, "Status": "Status", "State": "State", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index d4d640d..01f7836 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -16,6 +16,13 @@ "Rejected": "已拒绝", "Published": "已发布" }, + "auditStateAction": { + "New": "新增", + "Pending": "待审核", + "Approved": "审核通过", + "Rejected": "审核拒绝", + "Published": "发布上线" + }, "Status": "状态", "State": "状态", diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index ecd1fe1..544c478 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -1,32 +1,53 @@ import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; -import { fetchJSON } from '@/utils/request'; +import { fetchJSON, postForm } from '@/utils/request'; import { HT_HOST } from '@/config'; import { groupBy } from '@/utils/commons'; -const searchAgencyAction = async (param) => { +export const searchAgencyAction = async (param) => { const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param); return errcode !== 0 ? [] : result; }; -const getAgencyProducts = async (param) => { - const { errcode, Result, Result1 } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param); +export const getAgencyProductsAction = async (param) => { + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/travel-agency-products`, param); + return errcode !== 0 ? [] : result; +}; + +export const postProductsQuoteAudit = async (auditState, quoteRow) => { + const postbody = { + audit_state: auditState, + id: quoteRow.id, + travel_agency_id: quoteRow.info.travel_agency_id, + }; + const formData = new FormData(); + Object.keys(postbody).forEach((key) => { + formData.append(key, postbody[key]); + }); + const json = await postForm(`${HT_HOST}/Service_BaseInfoWeb/travel-agency-products-quote-audit`, formData); + return json; + // return errcode !== 0 ? {} : result; +}; + +export const postProductsAudit = async (auditState, infoRow) => { + const postbody = { + audit_state: auditState, + id: infoRow.id, + travel_agency_id: infoRow.travel_agency_id, + }; + const formData = new FormData(); + Object.keys(postbody).forEach((key) => { + formData.append(key, postbody[key]); + }); + const json = await postForm(`${HT_HOST}/Service_BaseInfoWeb/travel-agency-products-audit`, formData); + return json; + // const { errcode, result } = json; + // return errcode !== 0 ? {} : result; }; const initialState = { loading: false, - agencyList: [ - { - 'audit_date': '2001-03-03', - 'travel_agency_name': '新油低外', - 'travel_agency_id': '650000200301029585', - 'created_by': '冯丽', - 'create_date': '1989-06-20', - 'lastedit_memo': 'nostrud ad eu', - 'audited_by': '黎静', - 'audit_state': '1', - }, - ], + agencyList: [], activeAgency: {}, agencyProducts: groupBy([ { @@ -280,6 +301,17 @@ export const useProductsStore = create( setAgencyList(res); setLoading(false); }, + + getAgencyProducts: async (param) => { + const { setLoading, setActiveAgency, setAgencyProducts } = get(); + setLoading(true); + const res = await getAgencyProductsAction(param); + const productsData = groupBy(res, row => row.info.type); + setAgencyProducts(productsData); + setActiveAgency(res[0].info); + setLoading(false); + }, + })) ); export default useProductsStore; diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index cbc8fdb..3a4f3e6 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -1,14 +1,31 @@ -import { createContext, useContext, useEffect, useState } from 'react'; -import { Link, useLocation, } from 'react-router-dom'; -import { Button, Collapse, Table, Tabs, Typography, Space, } from 'antd'; -import { useProductsTypes, useProductsAuditStatesMapVal, useProductsAuditStates } from '@/hooks/useProductsSets'; +import { useEffect, useState } from 'react'; +import { useParams, } from 'react-router-dom'; +import { App, Button, Collapse, Table, Space, } from 'antd'; +import { useProductsTypes, useProductsAuditStatesMapVal } from '@/hooks/useProductsSets'; import SecondHeaderWrapper from '@/components/SecondHeaderWrapper'; import { useTranslation } from 'react-i18next'; -import useProductsStore from '@/stores/Products/Index'; +import useProductsStore, { postProductsQuoteAudit } from '@/stores/Products/Index'; import { isEmpty } from '@/utils/commons'; -const Header = ({title, ...props}) => { +const Header = ({ title, agency, ...props}) => { const { t } = useTranslation(); + const { message, notification } = App.useApp(); + const handleAuditItem = (state, row) => { + postProductsQuoteAudit(state, row) + .then((json) => { + if (json.errcode === 0) { + message.success('✔'); + } + }) + .catch((ex) => { + notification.error({ + message: 'Notification', + description: ex.message, + placement: 'top', + duration: 4, + }); + }); + }; return (
@@ -16,15 +33,37 @@ const Header = ({title, ...props}) => {
{/* */} {/* */} - + + {/* todo: export */} +
); }; const PriceTable = ({dataSource, loading}) => { const { t } = useTranslation('products'); + const { message, notification } = App.useApp(); const stateMapVal = useProductsAuditStatesMapVal(); + const handleAuditPriceItem = (state, row) => { + postProductsQuoteAudit(state, row) + .then((json) => { + if (json.errcode === 0) { + message.success('✔'); + } + }) + .catch((ex) => { + notification.error({ + message: 'Notification', + description: ex.message, + placement: 'top', + duration: 4, + }); + }); + }; + const columns = [ { key: 'title', dataIndex: ['info', 'title'], title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }) }, { key: 'value', title: t('Quotation'), render: (_, { value, currency, unit }) => `${value} ${currency} / ${unit}` }, @@ -55,10 +94,10 @@ const PriceTable = ({dataSource, loading}) => { { title: '', key: 'action', - render: () => ( + render: (_, r) => ( - - + + ), }, @@ -76,11 +115,19 @@ const TypesPanels = () => { const [activeKey, setActiveKey] = useState([]); const [showTypes, setShowTypes] = useState([]); useEffect(() => { - // 只显示有产品的类型 + // 只显示有产品的类型; 展开产品的价格表, 合并名称列 const hasDataTypes = Object.keys(agencyProducts); const _show = productsTypes .filter((kk) => hasDataTypes.includes(kk.value)) - .map((ele) => ({ ...ele, children: r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i=== 0 ? c.quotation.length : 0 }))), [])} /> })); + .map((ele) => ({ + ...ele, + children: ( + r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i === 0 ? c.quotation.length : 0 }))), [])} + /> + ), + })); setShowTypes(_show); setActiveKey(isEmpty(_show) ? [] : [_show[0].key]); @@ -97,14 +144,24 @@ const TypesPanels = () => { } const Audit = ({ ...props }) => { - // const [loading, agencyProducts] = useProductsStore((state) => [state.loading, state.agencyProducts]); + const { agencyId: travel_agency_id } = useParams(); + const [activeAgency, getAgencyProducts] = useProductsStore((state) => [state.activeAgency, state.getAgencyProducts]); + + const handleGetAgencyProducts = () => { + getAgencyProducts({ travel_agency_id }); + } + + useEffect(() => { + handleGetAgencyProducts(); + + return () => {}; + }, [travel_agency_id]) return ( <> - }> + }>
- {/* */}
); From e3dc13bc6c160f21d11527bdab31f6d0a9f98efa Mon Sep 17 00:00:00 2001 From: YCC Date: Wed, 19 Jun 2024 16:10:48 +0800 Subject: [PATCH 15/24] =?UTF-8?q?=E6=9C=BA=E7=A5=A8=E8=AE=A1=E5=88=92?= =?UTF-8?q?=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/Airticket.js | 23 ++ src/utils/commons.js | 635 +++++++++++++++++----------------- src/views/airticket/Index.jsx | 22 +- src/views/airticket/Plan.jsx | 190 +++++++++- 4 files changed, 523 insertions(+), 347 deletions(-) diff --git a/src/stores/Airticket.js b/src/stores/Airticket.js index d342b83..6a393b9 100644 --- a/src/stores/Airticket.js +++ b/src/stores/Airticket.js @@ -8,6 +8,8 @@ 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, TimeStart, TimeEnd) { const { setLoading, setPlanList } = get(); @@ -24,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/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/views/airticket/Index.jsx b/src/views/airticket/Index.jsx index 2f5ccf9..32a84a5 100644 --- a/src/views/airticket/Index.jsx +++ b/src/views/airticket/Index.jsx @@ -2,7 +2,7 @@ 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, NavLink } from "react-router-dom"; -import { isEmpty } from "@/utils/commons"; +import { isEmpty, formatColonTime } from "@/utils/commons"; import dayjs from "dayjs"; import SearchForm from "@/components/SearchForm"; @@ -54,21 +54,19 @@ const planListColumns = [ title: "起飞时间", key: "FlightTimeStart", dataIndex: "FlightTimeStart", - render: text => { - const hours = text.substring(0, 2); - const minutes = text.substring(2); - return `${hours}:${minutes}`; - }, + render: text => formatColonTime(text), }, { title: "落地时间", key: "FlightTimeEnd", dataIndex: "FlightTimeEnd", - render: text => { - const hours = text.substring(0, 2); - const minutes = text.substring(2); - return `${hours}:${minutes}`; - }, + render: text => formatColonTime(text), + }, + { + title: "是否出票", + key: "COLD_PlanVEI_SN", + dataIndex: "COLD_PlanVEI_SN", + render: (text, record) => "否", }, { title: "操作", @@ -82,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} `; diff --git a/src/views/airticket/Plan.jsx b/src/views/airticket/Plan.jsx index 4cd4bf0..29ba956 100644 --- a/src/views/airticket/Plan.jsx +++ b/src/views/airticket/Plan.jsx @@ -1,40 +1,194 @@ import { useState, useEffect } from "react"; -import { Grid, Divider, Layout, Spin, Input, Col, Row, Space, List, Table, Button } from "antd"; -import { PhoneOutlined, CustomerServiceOutlined, AudioOutlined } from "@ant-design/icons"; +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 } from "@/utils/commons"; -import dayjs from "dayjs"; -import { HT_HOST, OFFICEWEBVIEWERURL } from "@/config"; +import { isEmpty, formatColonTime } from "@/utils/commons"; +import { OFFICEWEBVIEWERURL } from "@/config"; import airTicketStore from "@/stores/Airticket"; -import useAuthStore from "@/stores/Auth"; +import { usingStorage } from "@/hooks/usingStorage"; const AirticketPlan = props => { - const href = useHref(); - const navigate = useNavigate(); const { coli_sn } = useParams(); - const [travelAgencyId, loginToken] = useAuthStore( - state => state.loginUser.travelAgencyId, - state => state.loginUser.loginToken - ); - const [getPlanList, planList, loading] = airTicketStore(state => [state.getPlanList, state.planList, state.loading]); - const [loginUser] = useAuthStore(state => [state.loginUser]); + 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); - const reservationUrl = `https://p9axztuwd7x8a7.mycht.cn/service-fileServer/DownloadPlanDoc?GRI_SN=${coli_sn}&VEI_SN=${travelAgencyId}&token=${loginToken}&FileType=1`; + console.log(reservationPreviewUrl); - const reservationPreviewUrl = OFFICEWEBVIEWERURL + encodeURIComponent(reservationUrl); + const Airticket_form = props => { + const aitInfo = props.airInfo; + return ( + <> +
+ + + + + + + } value={aitInfo.FromCity} /> + } value={aitInfo.ToCity} /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {/* + + */} + + +
+ + ); + }; - useEffect(() => {}, []); + 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 ( - + {/* */} + 出票信息 + + + ); From a7c87170a0154d68314944ec7f35965214f54c09 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 20 Jun 2024 14:04:22 +0800 Subject: [PATCH 16/24] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E5=AE=A2=E6=9C=8D=E9=A6=96=E9=A1=B5,=20=E5=AE=A1?= =?UTF-8?q?=E6=A0=B8=E4=BA=A7=E5=93=81:=20=E6=A0=B8=E5=AF=B9=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/SearchForm.jsx | 6 +++--- src/hooks/useProductsSets.js | 25 ++++++++++++++++++++----- src/stores/Products/Index.js | 12 ++++++------ src/views/products/Audit.jsx | 12 +++++++----- 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/components/SearchForm.jsx b/src/components/SearchForm.jsx index 9f55352..6a53cac 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -15,8 +15,8 @@ import AuditStateSelector from './AuditStateSelector'; //供应商列表 export const fetchVendorList = async () => { - const { errcode, Result } = await fetchJSON(`${HT_HOST}/service-cusservice/PTGetHWVendorList`) - return errcode !== 0 ? [] : Result + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/VendorList`) + return errcode !== 0 ? [] : result } const { RangePicker } = DatePicker; @@ -212,7 +212,7 @@ function getFields(props) { 'agency', 99, - + , fieldProps?.agency?.col || 6 ), diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index f002991..93feaac 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -3,6 +3,21 @@ import { useTranslation } from 'react-i18next'; /** * 产品管理 相关的预设数据 + * 项目类型 +1 酒店预定 +2 火车 +3 飞机票务 +4 游船 +5 快巴 +6 旅行社(综费) +7 景点 +8 特殊项目 +9 其他 +A 酒店 +B 超公里 +C 餐费 +D 包价包 +X 站 */ export const useProductsTypes = () => { @@ -11,13 +26,13 @@ export const useProductsTypes = () => { useEffect(() => { const newData = [ - { label: t('products:type.Experience'), value: 'Experience', key: 'Experience' }, + { label: t('products:type.Experience'), value: '6', key: '6' }, { label: t('products:type.Car'), value: 'Car', key: 'Car' }, { label: t('products:type.Guide'), value: 'Guide', key: 'Guide' }, - { label: t('products:type.Package'), value: 'Package', key: 'Package' }, - { label: t('products:type.Attractions'), value: 'Attractions', key: 'Attractions' }, - { label: t('products:type.Meals'), value: 'Meals', key: 'Meals' }, - { label: t('products:type.Extras'), value: 'Extras', key: 'Extras' }, + { label: t('products:type.Package'), value: 'D', key: 'D' }, + { label: t('products:type.Attractions'), value: '7', key: '7' }, + { label: t('products:type.Meals'), value: 'C', key: 'C' }, + { label: t('products:type.Extras'), value: '8', key: '8' }, { label: t('products:type.Special'), value: 'Special', key: 'Special' }, ]; setTypes(newData); diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 544c478..51d3010 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -10,21 +10,21 @@ export const searchAgencyAction = async (param) => { return errcode !== 0 ? [] : result; }; export const getAgencyProductsAction = async (param) => { - const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/travel-agency-products`, param); - return errcode !== 0 ? [] : result; + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/travel_agency_products`, param); + return errcode !== 0 ? { agency: {}, products: [] } : result; }; export const postProductsQuoteAudit = async (auditState, quoteRow) => { const postbody = { audit_state: auditState, id: quoteRow.id, - travel_agency_id: quoteRow.info.travel_agency_id, + travel_agency_id: quoteRow.travel_agency_id, }; const formData = new FormData(); Object.keys(postbody).forEach((key) => { formData.append(key, postbody[key]); }); - const json = await postForm(`${HT_HOST}/Service_BaseInfoWeb/travel-agency-products-quote-audit`, formData); + const json = await postForm(`${HT_HOST}/Service_BaseInfoWeb/quotation_audit`, formData); return json; // return errcode !== 0 ? {} : result; }; @@ -306,9 +306,9 @@ export const useProductsStore = create( const { setLoading, setActiveAgency, setAgencyProducts } = get(); setLoading(true); const res = await getAgencyProductsAction(param); - const productsData = groupBy(res, row => row.info.type); + const productsData = groupBy(res.products, row => row.info.product_type_id); setAgencyProducts(productsData); - setActiveAgency(res[0].info); + setActiveAgency(res.agency); setLoading(false); }, diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index 3a4f3e6..16cefc9 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -9,9 +9,10 @@ import { isEmpty } from '@/utils/commons'; const Header = ({ title, agency, ...props}) => { const { t } = useTranslation(); + const [activeAgency, ] = useProductsStore((state) => [state.activeAgency]); const { message, notification } = App.useApp(); const handleAuditItem = (state, row) => { - postProductsQuoteAudit(state, row) + postProductsQuoteAudit(state, {id: row.id, travel_agency_id: activeAgency.travel_agency_id}) .then((json) => { if (json.errcode === 0) { message.success('✔'); @@ -44,11 +45,12 @@ const Header = ({ title, agency, ...props}) => { const PriceTable = ({dataSource, loading}) => { const { t } = useTranslation('products'); + const [activeAgency, ] = useProductsStore((state) => [state.activeAgency]); const { message, notification } = App.useApp(); const stateMapVal = useProductsAuditStatesMapVal(); const handleAuditPriceItem = (state, row) => { - postProductsQuoteAudit(state, row) + postProductsQuoteAudit(state, {id: row.id, travel_agency_id: activeAgency.travel_agency_id}) .then((json) => { if (json.errcode === 0) { message.success('✔'); @@ -66,7 +68,7 @@ const PriceTable = ({dataSource, loading}) => { const columns = [ { key: 'title', dataIndex: ['info', 'title'], title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }) }, - { key: 'value', title: t('Quotation'), render: (_, { value, currency, unit }) => `${value} ${currency} / ${unit}` }, + { key: 'value', title: t('Quotation'), render: (_, { value, currency, unit_name }) => `${value} ${currency} / ${unit_name}` }, // {key: 'price', title: t('Currency'), }, // {key: 'currency', title: t('Currency'), }, // {key: 'unit', title: t('Unit'), }, @@ -88,7 +90,7 @@ const PriceTable = ({dataSource, loading}) => { key: 'state', title: t('State'), render: (_, r) => { - return stateMapVal[`${r.info.audit_state}`]?.label; + return stateMapVal[`${r.audit_state_id}`]?.label; }, }, { @@ -144,7 +146,7 @@ const TypesPanels = () => { } const Audit = ({ ...props }) => { - const { agencyId: travel_agency_id } = useParams(); + const { travel_agency_id } = useParams(); const [activeAgency, getAgencyProducts] = useProductsStore((state) => [state.activeAgency, state.getAgencyProducts]); const handleGetAgencyProducts = () => { From a61ef8882baabc51b483b1b0555644a0bbb39daa Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 21 Jun 2024 09:38:35 +0800 Subject: [PATCH 17/24] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=8F=82=E6=95=B0:=20wu=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useProductsSets.js | 14 +++++++------- src/stores/Auth.js | 1 + src/views/App.jsx | 3 ++- src/views/products/Audit.jsx | 4 ++-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 93feaac..0994b3f 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -9,13 +9,13 @@ import { useTranslation } from 'react-i18next'; 3 飞机票务 4 游船 5 快巴 -6 旅行社(综费) -7 景点 -8 特殊项目 +6 旅行社(综费) - +7 景点 - +8 特殊项目 - 9 其他 A 酒店 -B 超公里 -C 餐费 +B 超公里 - +C 餐费 - D 包价包 X 站 */ @@ -33,7 +33,7 @@ export const useProductsTypes = () => { { label: t('products:type.Attractions'), value: '7', key: '7' }, { label: t('products:type.Meals'), value: 'C', key: 'C' }, { label: t('products:type.Extras'), value: '8', key: '8' }, - { label: t('products:type.Special'), value: 'Special', key: 'Special' }, + // { label: t('products:type.Special'), value: 'Special', key: 'Special' }, ]; setTypes(newData); }, [i18n.language]); @@ -51,7 +51,7 @@ export const useProductsAuditStates = () => { { key: '-1', value: '-1', label: t('products:auditState.New') }, { key: '0', value: '0', label: t('products:auditState.Pending') }, { key: '2', value: '2', label: t('products:auditState.Approved') }, - // { key: '3', value: '3', label: t('products:auditState.Rejected') }, + { key: '3', value: '3', label: t('products:auditState.Rejected') }, { key: '1', value: '1', label: t('products:auditState.Published') }, ]; setTypes(newData); diff --git a/src/stores/Auth.js b/src/stores/Auth.js index f7f31f3..5511e6b 100644 --- a/src/stores/Auth.js +++ b/src/stores/Auth.js @@ -88,6 +88,7 @@ const useAuthStore = create((set, get) => ({ setStorage(KEY_TRAVEL_AGENCY_ID, userDetail.LMI_VEI_SN) setStorage(KEY_USER_DETAIL, {username: userDetail.LoginName, travelAgencyName: userDetail.VName}) appendRequestParams('token', loginToken) + appendRequestParams('wu_id', userDetail.LMI_SN) // loadPageSpy(`${json.Result.VName}-${json.Result.LoginName}`) startTokenInterval() }, diff --git a/src/views/App.jsx b/src/views/App.jsx index 0edd221..b61c1ce 100644 --- a/src/views/App.jsx +++ b/src/views/App.jsx @@ -28,7 +28,7 @@ function App() { const [validateUserPassword, tokenTimeout] = useAuthStore( (state) => [state.validateUserPassword, state.tokenTimeout]) - const { loginToken, userDetail } = usingStorage() + const { loginToken, userDetail, userId } = usingStorage() const noticeUnRead = useNoticeStore((state) => state.noticeUnRead) const href = useHref() @@ -40,6 +40,7 @@ function App() { if (!needToLogin) { appendRequestParams('token', loginToken) + appendRequestParams('wu_id', userId) } useEffect(() => { diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index 16cefc9..c548de2 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -96,12 +96,12 @@ const PriceTable = ({dataSource, loading}) => { { title: '', key: 'action', - render: (_, r) => ( + render: (_, r) => r.audit_state_id <= 0 ?( - ), + ) : null, }, ]; return
r.id} />; From e9b7aec5219c997bc6acce03d232c99d83237ecb Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 21 Jun 2024 14:20:06 +0800 Subject: [PATCH 18/24] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E6=90=9C=E7=B4=A2+=E5=B9=B4=E4=BB=BD,=E7=8A=B6?= =?UTF-8?q?=E6=80=81;=20=E4=BA=A7=E5=93=81=E7=B1=BB=E5=9E=8B=E6=8E=92?= =?UTF-8?q?=E5=BA=8F;=20=E5=AE=A1=E6=A0=B8=E9=A1=B5:=20=E6=88=90=E4=BA=BA,?= =?UTF-8?q?=E5=84=BF=E7=AB=A5=E4=BB=B7;=20=E8=AF=AD=E7=A7=8D=E5=88=97?= =?UTF-8?q?=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/products.json | 5 +- public/locales/zh/products.json | 7 +- src/components/SearchForm.jsx | 35 ++- src/components/SearchInput.jsx | 1 + .../{usePresets.js => useDatePresets.js} | 4 +- src/hooks/useLanguageSets.js | 14 ++ src/hooks/useProductsSets.js | 1 + src/main.jsx | 4 +- src/stores/Products/Index.js | 236 +----------------- src/views/products/Audit.jsx | 51 ++-- src/views/products/Index.jsx | 27 +- 11 files changed, 111 insertions(+), 274 deletions(-) rename src/hooks/{usePresets.js => useDatePresets.js} (91%) create mode 100644 src/hooks/useLanguageSets.js diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 6033e89..b5e7694 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -7,6 +7,7 @@ "Attractions": "Attractions", "Meals": "Meals", "Extras": "Extras", + "Overtravel": "超公里", "Special": "Special" }, "auditState": { @@ -20,7 +21,7 @@ "New": "New", "Pending": "Pending", "Approved": "Approve", - "Rejected": "Rejecte", + "Rejected": "Reject", "Published": "Publish" }, "Status": "Status", @@ -42,6 +43,8 @@ "UseDates": "Use Dates", "Weekdays": "Weekdays", + "UseYear": "Use Year", + "AgeType": { "Type": "Age Type", "Adult": "Adult", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 01f7836..9f1f8b6 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -6,7 +6,8 @@ "Package": "包价线路", "Attractions": "景点", "Meals": "餐费", - "Extras": "附加", + "Extras": "附加项目", + "Overtravel": "超公里", "Special": "特殊项目" }, "auditState": { @@ -21,7 +22,7 @@ "Pending": "待审核", "Approved": "审核通过", "Rejected": "审核拒绝", - "Published": "发布上线" + "Published": "审核发布" }, "Status": "状态", "State": "状态", @@ -41,6 +42,8 @@ "UseDates": "使用日期", "Weekdays": "有效日/周X", + "UseYear": "年份", + "AgeType": { "Type": "人群", "Adult": "成人", diff --git a/src/components/SearchForm.jsx b/src/components/SearchForm.jsx index 6a53cac..a669294 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -3,7 +3,7 @@ import { Form, Input, Row, Col, Select, DatePicker, Space, Button } from 'antd'; import { objectMapper, at } from '@/utils/commons'; import { DATE_FORMAT, SMALL_DATETIME_FORMAT } from '@/config'; import useFormStore from '@/stores/Form'; -import usePresets from '@/hooks/usePresets'; +import useDatePresets from '@/hooks/useDatePresets'; import { useTranslation } from 'react-i18next'; import { fetchJSON } from '@/utils/request'; @@ -14,8 +14,8 @@ import AuditStateSelector from './AuditStateSelector'; //供应商列表 -export const fetchVendorList = async () => { - const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/VendorList`) +export const fetchVendorList = async (q) => { + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/VendorList`, { q }) return errcode !== 0 ? [] : result } @@ -23,7 +23,7 @@ const { RangePicker } = DatePicker; const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => { const { t } = useTranslation(); - const presets = usePresets(); + const presets = useDatePresets(); const [formValues, setFormValues] = useFormStore((state) => [state.formValues, state.setFormValues]); const [formValuesToSub, setFormValuesToSub] = useFormStore((state) => [state.formValuesToSub, state.setFormValuesToSub]); const [form] = Form.useForm(); @@ -57,6 +57,9 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => { return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.value : ''; }, }, + 'year': [ + { key: 'year', transform: (arrVal) => (arrVal ? arrVal.format('YYYY') : '') }, + ], }; let dest = {}; const { dates, ...omittedValue } = values; @@ -165,15 +168,15 @@ function getFields(props) { 'referenceNo', 99, - + , fieldProps?.referenceNo?.col || 6 ), item( 'PNR', 99, - - + + , fieldProps?.PNR?.col || 4 ), @@ -208,11 +211,26 @@ function getFields(props) { /** * */ + item( + 'year', + 99, + + + , + fieldProps?.year?.col || 3 + ), item( 'agency', 99, - + , fieldProps?.agency?.col || 6 ), @@ -224,7 +242,6 @@ function getFields(props) { , fieldProps?.audit_state?.col || 3 ), - ]; baseChildren = baseChildren .map((x) => { diff --git a/src/components/SearchInput.jsx b/src/components/SearchInput.jsx index d816647..dcf2657 100644 --- a/src/components/SearchInput.jsx +++ b/src/components/SearchInput.jsx @@ -31,6 +31,7 @@ function DebounceSelect({ fetchOptions, debounceTimeout = 800, ...props }) { showSearch allowClear maxTagCount={1} + dropdownStyle={{width: '16rem'}} {...props} onSearch={debounceFetcher} notFoundContent={fetching ? : null} diff --git a/src/hooks/usePresets.js b/src/hooks/useDatePresets.js similarity index 91% rename from src/hooks/usePresets.js rename to src/hooks/useDatePresets.js index aa046e6..87397b9 100644 --- a/src/hooks/usePresets.js +++ b/src/hooks/useDatePresets.js @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'; import dayjs from "dayjs"; import { useTranslation } from 'react-i18next'; -const usePresets = () => { +const useDatePresets = () => { const [presets, setPresets] = useState([]); const { t, i18n } = useTranslation(); @@ -39,4 +39,4 @@ const usePresets = () => { return presets; } -export default usePresets; +export default useDatePresets; diff --git a/src/hooks/useLanguageSets.js b/src/hooks/useLanguageSets.js new file mode 100644 index 0000000..1dab4b1 --- /dev/null +++ b/src/hooks/useLanguageSets.js @@ -0,0 +1,14 @@ +export const useLanguageSets = () => { + const newData = [ + { key: '1', value: '1', label: 'English' }, + { key: '2', value: '2', label: 'Chinese (中文)' }, + { key: '3', value: '3', label: 'Japanese (日本語)' }, + { key: '4', value: '4', label: 'German (Deutsch)' }, + { key: '5', value: '5', label: 'French (Français)' }, + { key: '6', value: '6', label: 'Spanish (Español)' }, + { key: '7', value: '7', label: 'Russian (Русский)' }, + { key: '8', value: '8', label: 'Italian (Italiano)' }, + ]; + + return newData; +}; diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 0994b3f..5f3a85b 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -27,6 +27,7 @@ export const useProductsTypes = () => { useEffect(() => { const newData = [ { label: t('products:type.Experience'), value: '6', key: '6' }, + { label: t('products:type.Overtravel'), value: 'B', key: 'B' }, { label: t('products:type.Car'), value: 'Car', key: 'Car' }, { label: t('products:type.Guide'), value: 'Guide', key: 'Guide' }, { label: t('products:type.Package'), value: 'D', key: 'D' }, diff --git a/src/main.jsx b/src/main.jsx index 3733462..a4556b5 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -69,8 +69,8 @@ const router = createBrowserRouter([ { path: "invoice/paid/detail/:flid",element:}, { path: "airticket",element:}, { path: "products",element:}, - { path: "products/:travel_agency_id/audit",element:}, - { path: "products/:travel_agency_id",element:}, + { path: "products/:travel_agency_id/:use_year/:audit_state/audit",element:}, + { path: "products/:travel_agency_id/:use_year/:audit_state/edit",element:}, ] }, { diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 51d3010..a18f098 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -10,7 +10,8 @@ export const searchAgencyAction = async (param) => { return errcode !== 0 ? [] : result; }; export const getAgencyProductsAction = async (param) => { - const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/travel_agency_products`, param); + const _param = { ...param, use_year: (param.use_year || '').replace('all', ''), audit_state: (param.audit_state || '').replace('all', '') }; + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/travel_agency_products`, _param); return errcode !== 0 ? { agency: {}, products: [] } : result; }; @@ -47,238 +48,10 @@ export const postProductsAudit = async (auditState, infoRow) => { const initialState = { loading: false, + searchValues: {}, agencyList: [], activeAgency: {}, - agencyProducts: groupBy([ - { - "info": { - "id": "640000198509289851", - "title": "如拉下完公", - "code": "grlkt", - "type": "Guide", - "audit_state": "1", - "create_date": "2022-01-13", - "created_by": "郝涛", - "travel_agency_id": "710000200712195349", - "travel_agency_name": "国得气验", - "lastedit_memo": "划百引程级门会需代领主属快。", - "remarks": "及决对金利低集小理电和按常如门。", - "duration": 2, - "duration_unit": "m", - "open_weekdays": "6", - "recommends_rate": 3, - "dept": 1, - "display_to_c": "2", - "km": 27, - "city_id": 77, - "city_name": "称命" - }, - "quotation": [ - { - "id": "21000020030611324X", - "value": 70, - "currency": "CNY", - "unit": "团", - "age_type": "儿童", - "group_size_min": 4, - "group_size_max": 4, - "use_dates_start": "2004-01-19", - "use_dates_end": "1990-03-10", - "weekdays": "4", - "audit_state": "ea pariatur", - "lastedit_memo": "sunt" - }, - { - "id": "610000197306240177", - "value": 86, - "currency": "CNY", - "unit": "人", - "age_type": "儿童", - "group_size_min": 6, - "group_size_max": 8, - "use_dates_start": "1996-12-16", - "use_dates_end": "1974-11-19", - "weekdays": "4", - "audit_state": "aliqua aute quis ipsum", - "lastedit_memo": "commodo adipisicing ea ipsum" - } - ], - "lgc_details": [ - { - "lgc": "mollit", - "title": "林运但", - "description": "学克信图走法因心委周说步将且文手越。", - "id": "35" - }, - { - "lgc": "et laborum", - "title": "备上引深量知量", - "description": "到听少文话包由北层中争二调原务越明在。", - "id": "74" - }, - { - "lgc": "minim velit", - "title": "安都始新", - "description": "取影压前手府要青白支大而。", - "id": "23" - } - ] - }, - { - "info": { - "id": "41000019901227754X", - "title": "据划京少国取", - "code": "ore", - "type": "Guide", - "audit_state": "2", - "create_date": "1979-01-31", - "created_by": "陆芳", - "travel_agency_id": "110000198612200137", - "travel_agency_name": "少平酸型", - "lastedit_memo": "八想军也装运知长示各院步济水。", - "remarks": "千改原统实专回列参目党却是样与后收。", - "duration": 3, - "duration_unit": "d", - "open_weekdays": "5", - "recommends_rate": 5, - "dept": 1, - "display_to_c": "2", - "km": 30, - "city_id": 62, - "city_name": "业入" - }, - "quotation": [ - { - "id": "37000019760525515X", - "value": 93, - "currency": "CNY", - "unit": "团", - "age_type": "成人", - "group_size_min": 7, - "group_size_max": 11, - "use_dates_start": "1992-11-22", - "use_dates_end": "1997-07-16", - "weekdays": "7", - "audit_state": "id nulla irure cupidatat", - "lastedit_memo": "quis aute reprehenderit consectetur" - }, - { - "id": "150000199506023175", - "value": 90, - "currency": "CNY", - "unit": "人", - "age_type": "儿童", - "group_size_min": 9, - "group_size_max": 10, - "use_dates_start": "2007-09-11", - "use_dates_end": "2013-07-27", - "weekdays": "5", - "audit_state": "commodo ad ut", - "lastedit_memo": "id anim incididunt" - } - ], - "lgc_details": [ - { - "lgc": "adipisicing elit Excepteur in", - "title": "很很结龙认", - "description": "事起复京长立然将采共层列工。", - "id": "43" - }, - { - "lgc": "dolore fugiat", - "title": "专中小", - "description": "示史想当集认点离反而原化精满并计前。", - "id": "28" - }, - { - "lgc": "sunt consectetur ea cillum", - "title": "他率带没", - "description": "节经厂面际是统表王活基书色活至是干验。", - "id": "83" - }, - { - "lgc": "incididunt labore fugiat", - "title": "精话西改", - "description": "须事金性别民学少拉个且须专需断连。", - "id": "97" - }, - { - "lgc": "dolore id", - "title": "文技话", - "description": "上任成条到则查支外很素给务府三。", - "id": "99" - } - ] - }, - { - "info": { - "id": "44000019990112280X", - "title": "节到和", - "code": "ixlmndtmz", - "type": "Meals", - "audit_state": "1", - "create_date": "2006-12-30", - "created_by": "易敏", - "travel_agency_id": "640000197111288408", - "travel_agency_name": "术备带走", - "lastedit_memo": "认队什教调问传改万消然声地全。", - "remarks": "属须厂几问总识看部群该克员方。", - "duration": 2, - "duration_unit": "m", - "open_weekdays": "6", - "recommends_rate": 3, - "dept": 2, - "display_to_c": "1", - "km": 13, - "city_id": 55, - "city_name": "铁以" - }, - "quotation": [ - { - "id": "13000019860219219X", - "value": 88, - "currency": "CNY", - "unit": "团", - "age_type": "儿童", - "group_size_min": 2, - "group_size_max": 4, - "use_dates_start": "1991-03-19", - "use_dates_end": "1974-03-13", - "weekdays": "3", - "audit_state": "officia voluptate ad adipisicing dolore", - "lastedit_memo": "Duis amet veniam enim" - }, - { - "id": "420000201706118123", - "value": 61, - "currency": "CNY", - "unit": "人", - "age_type": "儿童", - "group_size_min": 4, - "group_size_max": 10, - "use_dates_start": "1992-04-23", - "use_dates_end": "1970-07-19", - "weekdays": "5", - "audit_state": "commodo labore", - "lastedit_memo": "ullamco anim culpa do in" - } - ], - "lgc_details": [ - { - "lgc": "ut minim", - "title": "回等这意", - "description": "农满界个整千书得被写况空派会想头无。", - "id": "40" - }, - { - "lgc": "laborum id elit irure commodo", - "title": "增正数白养土子", - "description": "么划才共别程以元于族完难变。", - "id": "84" - } - ] - } -], row => row.info.type), + agencyProducts: {}, }; export const useProductsStore = create( devtools((set, get) => ({ @@ -287,6 +60,7 @@ export const useProductsStore = create( // state actions setLoading: loading => set({ loading }), + setSearchValues: searchValues => set({ searchValues }), setAgencyList: (agencyList) => set({ agencyList }), setActiveAgency: activeAgency => set({ activeAgency }), setAgencyProducts: agencyProducts => set({ agencyProducts }), diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index c548de2..e055cc3 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -7,15 +7,18 @@ import { useTranslation } from 'react-i18next'; import useProductsStore, { postProductsQuoteAudit } from '@/stores/Products/Index'; import { isEmpty } from '@/utils/commons'; -const Header = ({ title, agency, ...props}) => { +const Header = ({ title, agency, refresh, ...props}) => { const { t } = useTranslation(); - const [activeAgency, ] = useProductsStore((state) => [state.activeAgency]); + const [activeAgency, ] = useProductsStore((state) => [state.activeAgency, ]); const { message, notification } = App.useApp(); const handleAuditItem = (state, row) => { postProductsQuoteAudit(state, {id: row.id, travel_agency_id: activeAgency.travel_agency_id}) .then((json) => { if (json.errcode === 0) { - message.success('✔'); + message.success(json.errmsg); + if (typeof refresh === 'function') { + refresh(); + } } }) .catch((ex) => { @@ -34,8 +37,14 @@ const Header = ({ title, agency, ...props}) => { {/* */} {/* */} - + {/* */} + {/* todo: export */} @@ -43,9 +52,9 @@ const Header = ({ title, agency, ...props}) => { ); }; -const PriceTable = ({dataSource, loading}) => { +const PriceTable = ({dataSource,refresh}) => { const { t } = useTranslation('products'); - const [activeAgency, ] = useProductsStore((state) => [state.activeAgency]); + const [loading, activeAgency, ] = useProductsStore((state) => [state.loading, state.activeAgency, ]); const { message, notification } = App.useApp(); const stateMapVal = useProductsAuditStatesMapVal(); @@ -53,7 +62,10 @@ const PriceTable = ({dataSource, loading}) => { postProductsQuoteAudit(state, {id: row.id, travel_agency_id: activeAgency.travel_agency_id}) .then((json) => { if (json.errcode === 0) { - message.success('✔'); + message.success(json.errmsg); + if (typeof refresh === 'function') { + refresh(); + } } }) .catch((ex) => { @@ -68,11 +80,11 @@ const PriceTable = ({dataSource, loading}) => { const columns = [ { key: 'title', dataIndex: ['info', 'title'], title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }) }, - { key: 'value', title: t('Quotation'), render: (_, { value, currency, unit_name }) => `${value} ${currency} / ${unit_name}` }, + { key: 'adult', title: t('AgeType.Adult'), render: (_, { value, currency, unit_name }) => `${value} ${currency} / ${unit_name}` }, + { key: 'child', title: t('AgeType.Child'), render: (_, { value, currency, unit_name }) => `${value} ${currency} / ${unit_name}` }, // {key: 'price', title: t('Currency'), }, // {key: 'currency', title: t('Currency'), }, // {key: 'unit', title: t('Unit'), }, - { key: 'ageType', dataIndex: ['age_type'], title: t('AgeType.Type') }, { key: 'groupSize', dataIndex: ['group_size_min'], @@ -94,24 +106,24 @@ const PriceTable = ({dataSource, loading}) => { }, }, { - title: '', + title: '价格审核', key: 'action', render: (_, r) => r.audit_state_id <= 0 ?( - + ) : null, }, ]; - return
r.id} />; + return
r.id} />; } /** * */ -const TypesPanels = () => { - const [loading, agencyProducts] = useProductsStore((state) => [state.loading, state.agencyProducts]); +const TypesPanels = (props) => { + const [loading, agencyProducts, ] = useProductsStore((state) => [state.loading, state.agencyProducts]); // console.log(agencyProducts); const productsTypes = useProductsTypes(); const [activeKey, setActiveKey] = useState([]); @@ -125,8 +137,9 @@ const TypesPanels = () => { ...ele, children: ( r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i === 0 ? c.quotation.length : 0 }))), [])} + refresh={props.refresh} /> ), })); @@ -146,11 +159,11 @@ const TypesPanels = () => { } const Audit = ({ ...props }) => { - const { travel_agency_id } = useParams(); + const { travel_agency_id, use_year, audit_state } = useParams(); const [activeAgency, getAgencyProducts] = useProductsStore((state) => [state.activeAgency, state.getAgencyProducts]); const handleGetAgencyProducts = () => { - getAgencyProducts({ travel_agency_id }); + getAgencyProducts({ travel_agency_id, use_year, audit_state }); } useEffect(() => { @@ -161,9 +174,9 @@ const Audit = ({ ...props }) => { return ( <> - }> + }>
- +
); diff --git a/src/views/products/Index.jsx b/src/views/products/Index.jsx index 02d57ec..5a6385c 100644 --- a/src/views/products/Index.jsx +++ b/src/views/products/Index.jsx @@ -11,16 +11,18 @@ import { objectMapper } from '@/utils/commons'; function Index() { const { t } = useTranslation(); const [loading, agencyList, searchAgency] = useProductsStore((state) => [state.loading, state.agencyList, state.searchAgency]); + const [searchValues, setSearchValues] = useProductsStore((state) => [state.searchValues, state.setSearchValues]); const formValuesToSub = useFormStore(state => state.formValuesToSub); const handleSearchAgency = (formVal = undefined) => { const { starttime, endtime, ...param } = formVal || formValuesToSub; const searchParam = objectMapper(param, { agency: 'travel_agency_id', startdate: 'edit_date1', enddate: 'edit_date2' }); + setSearchValues(searchParam); searchAgency(searchParam); } useEffect(() => { - handleSearchAgency(); + // handleSearchAgency(); }, []); const showTotal = (total) => t('Table.Total', { total }); @@ -37,8 +39,8 @@ function Index() { key: 'action', render: (_, r) => ( - {t('Edit')} - {t('Audit')} + {t('Edit')} + {t('Audit')} ), }, @@ -47,19 +49,21 @@ function Index() { { handleSearchAgency(formVal); @@ -67,7 +71,14 @@ function Index() { />
-
+
From cacc4832c5ce355b0bf179cfd6675b5a6c867544 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 21 Jun 2024 15:58:33 +0800 Subject: [PATCH 19/24] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E6=90=9C=E7=B4=A2:=20=E5=A4=9A=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Index.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/products/Index.jsx b/src/views/products/Index.jsx index 5a6385c..d1b1495 100644 --- a/src/views/products/Index.jsx +++ b/src/views/products/Index.jsx @@ -16,7 +16,7 @@ function Index() { const handleSearchAgency = (formVal = undefined) => { const { starttime, endtime, ...param } = formVal || formValuesToSub; - const searchParam = objectMapper(param, { agency: 'travel_agency_id', startdate: 'edit_date1', enddate: 'edit_date2' }); + const searchParam = objectMapper(param, { agency: 'travel_agency_ids', startdate: 'edit_date1', enddate: 'edit_date2' }); setSearchValues(searchParam); searchAgency(searchParam); } @@ -49,14 +49,14 @@ function Index() { Date: Fri, 21 Jun 2024 16:55:22 +0800 Subject: [PATCH 20/24] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E4=BB=B7?= =?UTF-8?q?=E6=A0=BC=E7=BC=96=E8=BE=91=E7=95=8C=E9=9D=A2=EF=BC=9B=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E7=BC=96=E8=BE=91=E8=A7=92=E8=89=B2=EF=BC=9B=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E8=A7=92=E8=89=B2=EF=BC=9B=E9=87=8D=E7=BD=AE=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/价格管理平台.bmpr | Bin 272384 -> 276480 bytes src/stores/Account.js | 45 ++++++++---- src/views/account/Management.jsx | 122 ++++--------------------------- src/views/account/RoleList.jsx | 46 ++++++------ 4 files changed, 68 insertions(+), 145 deletions(-) diff --git a/doc/价格管理平台.bmpr b/doc/价格管理平台.bmpr index e1065f737e4b4c4b23473125fbab526ac344148e..eb1a8d6ac17c14b1cafbeb9b654b686874de0f72 100644 GIT binary patch delta 57794 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 delta 55638 zcmc)Tdyu8;RUY=;-6QEp5g-d8Bm`JegyN_V^!-LsMAOqVeVxAF0YyFCJ$;|P-^SQm zqADpI9~+4lYHWl#r2L0XWF#U-4&%NOTzx0L=z3prMMy=NHx>~LF=ktEUhyO%R z!{7Mn_doNR!M57HT2HO3)>iAt|Gl+uueH~1*9P+W+w-kA-&*sjyVjQ@oz<^*^S3kS zbXRA!=lX$K=hY`(^RCyl<*c4s$ESYzHSc--n_tuPk8-ElO4Gk?`j<`rsOfi_{%O;1 zH~q$^-f?T9;Zs|G?zHjiYqfXZ$_Yz^T6k^#>wV@^zw?b>_i+b1t|ylohCX%h^UpP2 zedytPuD|0WO<#1p^Xl#2K6d@ZPd0YH>Ysn_>&E}q)zAIU|HbviPdC2t$*qqzJ=bvc z$J zN5fmLKlbOEzO|{n)_eUsA8*`x^1uIurdth7UA4ZeKla~${nf#5z52;N{fkX+YPkBP z&wcIH^8?>?J^A}hzwF}ftM~uZd#<0qZhG(42UfrJhyLpOp1k3UO`mJHI{0V(*U!&2 zJeT|S)Y_~2`kVVc^Aq2l>w0=OuRrtFhHt%k`)@yc{nvlA>CM-_`Xf!>e0|W{@aC%z z{oWg|p8q@BRTZ6d>&b58t&cR6I==Z$w_e}SloFo2;~Q@MdgIl558rwH?oT$3T~B_Z z@tqB=*M~pY@Q&+`i@ZoRIl;U{VhKhgNnrXNbbrjJjaMK0&@)$`{M#35)qeHSU;F)A&%f@OXFm1OU*CII!=0<) zUmL!D!&1W!eCoMhm~OZp`CQZMuDP;K|f(<0qP~zIgfS zSGA|sJ=bl=jm=lj|Cu+xsyk)gy}JG2&XbQEH10NCO^!zB=Nq5>=yMIvJehyK@vk*~ z_`kaUB<$jt~cLw>pfSKUGKU+cz?rtTGwkUwRhH@&HFrsH7%2V z?t>rM>aN|%=)99rdfV~-7moK`O}=gR`fVR<_^GS6|H2zzU#m5~v;6Yohxz60IneMg zUpVmAn^QMm|9{k9_u6vwW3RnAs(^3SDDVIDT0_%Y8a{mW-k+O(^6c+6{bbXpKhV(l z*WZ_)YPGM+`^WPBP~Lwr?;p+kV|o8_-k-_)vw8nU-v2o7-^%;9BS8K% z{rh=8U)!l&q>8tz*Mr*7_4_~6^tO6`Z9az|<~39sOG}K`wrVYT?c~V!)E=e&ojKN4 zo37o@8SDAplC$pA&T2EY13@re=j1$S#349 zCEQ|`jp6*S(A#;hWkGqE^E*;XSN`wIHIMVDr*@L}THamNVsnvlF7nIcT74ilzKpE* zYkSqfi=6VHb`+`SbL-aJc&(E3IDdz8Y&W%E$Ss?5)?`Gu$W6}k{a$KwkaikP-KHzy z4r|M~;ZR!VZpwR{l16HSxppG zqWb+T^|;KXkE@!sq~N=``QwzZ5kaPM?t|*gJL$gJ9No#;QxR!5w>?bF$5P3qO1QnW z_(n>pcjWMRWZ90axAXtQNVlH4YVyZ9HkNl!#=w4Rcaa;kr-;2=^QaPTAxFNvH(s9n}`>Q>~t%;EXG7Axs?^XW84 zFLIiuZH`zEQy*>9QN8*j^m&er=cw03uAfPp841hzyOZnM^650^Or}jHa_oNYw3MyrX^LIT|AxtFq!3JtjcW7&=^T|ALR8oO|esr&IhU9aR$@<{9h)BY10u|Msu_+ zpVv}WYwkareRVA*jpo{x$Y$;zrP;gd`Ffh4y7KBxb*3^Lma2B|N)aO|!d&XhIp-0~ za=e@)M(j!TZ6F2rrcQJDJC`bK=5{@~YCqlISM|nLt|Z)8&e)A8JJlG~b0ayoZnE}f zL@nlou3UJW%S_|t6xW@$zMT;^mEZPq)L4BOVYU2bj`ioPvmC#lBiN%H zJ&GVb>HhIt+mp(y=NF@TG%eJUa+Y%BK|Lps&IK)L+=HrKmc*9az9&ufAU84O7jmg3 zU^{=8Q^Z9Y^(5V}luupN`NryO8g@7BagfG+oJvilo*NN&HOC&N15FGSTuDhsMr3zY zw3?k(vu-2>PN(nhq^NsoM8fr^*#=Ua`AE#Y6l;ARh+vyJ)=>%GmMhjW^!M_2HAl7E zQfj}GYphIO+qtfc((`mnrdd5wwC0r6oTR}1NN;^QO(DY(h%m;)NRIjYFqiM-wVdzk zm2gw(BK`lk5?JdTL?}&v6w!wwnhv;3IWzgKKliXg?dAM~h){QO*@YhFlD;%?f11Ud z8_lVgDMAy@rrEYrfcoBxU^^*7VJGSTv((vb?x%~awAT2Ew3(J$&o35G%YtQx7J4_w zhEnN`9NVdL) z=j`Oy_6X3KTaBl*r5C!YIrV)I!7Yn-E8))b`!mbA$i98`$%k*b{%8NH>5W$>|Gf43 zPyDIIH$RDX^js5-;p%$w&o*>kf9>xz{rRh(9eL*JnI{jfU;X)}k%q48sn0hpto*C- z|9tb=TEqMP{^hf^Kly!scH!CD+iRP%%^T0w{^s~!uKR!ZPdfJ6jiL2FzM);dyY{`& zf!fjvUVgLV_vh0u?)=U1XVcnm&+vEr4G;4D`ZZr@>iL>4UzmY-{k>mkdh6GA=Zu~| zIPd!C3r)TMmebz0Id$^BKmH}JKPWO6wlM*&zwQf7Em!~T(l=dy@hIBsC-)kkxnBO) zP2aEHAG!X}pK5&f^>g2J>pP#k_cKk4jSZbwpZy=cECR6pJIOa>iHk;)@WA;$G4t*-&=0IziQQw zUjORbZoTXJ@n@T!ebV~&TOY3uUSGZ8>@C;t{k`bWRUf?Pxu%ab=0@GuhsTX?z52{| zzV7PzPrm!=z0ZE__1k~2>3>rEk6i!i4>rB)`qmGmoY@~}`b5*MzwpkS|BmMxzRh`0 zzVo8#?>0SY`o6}0(^SvtKiZN3V!_$VAhPSsXGk2U#ZA1s8A`rcoBSnzC1Wxp z*|3_+af_!t!Azkp+3!ur+-W0o%f(<%T>nL?1;z^2`%r&FA z*;*ygLfW9Gnq0P|uAJ4KUo0|bDaW!llj~@F(LWyMHq_pQ{J4{)VKf)?r`8Iv9$)6u zbVTUOStmKGJ?oqW)mpWmYbdxh&*q#_);~+i{fK1i-H51Nsos1#O>`l*rg5=O?BokMx|UlGMzn?W zw=pxGcTXy`7BTKcjOkphk!O&Z!+MC*t)NA0-kZ5CC3-dIcSe-)T3s`p zRsBV}(+F{rf{)Tf7ZIi_O>FaN%aQrif{LI8*7F|CO(^}RX^WMJK<_rdY^TQ&t!!Q= z`L#W5)0dKM$3wYJi_(5_c0HG~1BN0-M~e39i#R*E#dMC^?b;(d;YK6EcxpA4`gca8 zm7Hl9>9)=svjuJB)-;5%wA@KLuq9WYrVX^C8u#T$OUk;Jk_OVIb-mt|8|2@|jv~U{ zG|+HP9!#@6O2O-qUl-`#)%;H}+s+NxC?_d|mPE%ct=F8Jbf*kz1`T~Yt*CnRBc@Mx z?q*vy$ZhGF{}(xZKJ{FwI-xHzKF;l?b0d4)Xau3S@8@-t3lDOZu35^rk=%AG|2uM! zcNwV8G&*`y>yw;$mXZml;Q>9ZmGJXvm(`S9H=I^-JAK-prt8VK<1}My1Zb|LB>i}r zcRzL5NX-aGd%m9roX+2Y+?*Oo)jZGVnH*b5-Dr9P5sywVl(S7*3(-kBpeN^ewbUau z=_YdFc-mnmx7bRJMpBf;$fB{5OQ&+Im_HWM?)3Xs&fLuBq4e^0gqq5ko4KJg_3>G* zdX%>8&zW>$)3rP0Q(vtcYdN0rZ!y%ol&8Bne3s&7@?y%dCkc0$;*9;%G<$1qprsVb zj-Jb}m#LT8;MJX4j^vEal+c#C-mcE2be!ZIs>p*>>rw8on={($Y2=pFVmY-OO@YI? z+{!HIPq5jt`M3e##@YCQ!omLKKn z(FnVjx*w#hgN&h_Tr*P%M=zYo^&?fqI&-e|W-`|qU1^1SKAH%lssCDJAI+&#)oVHR zIm|#|4tC^Zrae7wB$w%`txCk^9JNqw=J-}_J(i&?$v)$Knm7U&5zn@!7zR+@(Sq7LgZs*fZ&O1y;-mWT@ z{ofLLTs7NLF0oGyrvZl|FO%UxhSzxNc`uh)3C8mOLDhW4sJK&g1!H6&zYeA(P=LjF zBL5Gi_Cc9a#fb=SQ34k1rZPs;MeR;KC!M8;w%n$y)@`YAe+pvsuSK{AX^XkMh9l;F z#MsHXpdMod*aPsOzCFyZ1G(nj3j>eAY9q7ZQtmn;_)N}j&$0Qmz-e{74rdDv6cP3# zr?vSsMRZltFQ$oh)06|bq(42sm|H!{?PpRSsM27r?N37=*psw4CvTYTGVNGj&rL}-mfM#F zNyDhJ54`ZTC~Po8Xq8doLkw+T+oqknrG=zi>48d#7 znZVJ7lxOL(YfMEHJHkXtxSjt8a>nhHzLz^K=Bk!dyS|pwsYviwM)X3O6z=99+=jwP z9UG09zSDOX@_8ZkD?1;|X*kypr|_M$V{c0HvM$<~M=QZC?L`TtUyr6Wm$XqmA8DoC zDWpI3u+0_=e<~MmMcTVLV>cJyNzoL~!wj3*9KFn^nf#s0^-PX~TtA%p9z+Ox#l5Nx zmhycu=M>W&o~fM%Q;oR@82x{vl5RF%mvVbcMPE*FbJqNFdTt}N-^-1xl%NJGs69?S z={XvCE=B6NvVAY65{waR;z7uq~JWzJduX!{lyP1_d%3^@Xr7y5iJ za#ES>4f|EyEWD31#vkNrdpqP6!n2ja=>!z;;fS=I-=WylbSn_EO43OF&g8EJX{>4utKOZ|%^FXD+1wmj z4Q7^4rPKdi6RZ9-X4W4vvFcA_X8j=( ztNt`*)*mvlDDQtnGwUx|lb920nU(CVsjTDOSqLpLhnbfa8fXQGh%rsoq)f9}HuH&E z1@~&rd7HT&lx$_Voii*kD_P&BvoujM?&LRSB^=iddT2WZo3p@S%nZY}tTuP5J*RlS z9^?qs**QF9tUPDj&E>3<8^}n3W4RvA!64a-7_9e#cRi{WDhLqA0oY+YzXVRP=+EX$ zF&maM3ux}3?(i5Q?c*Hj$+5}Y5aM+l*}Eg?e6^ah^a@CDnxpjJ*4$#S+5^_}+h|JW zxjE1Aa&#h91J!VSu7guZyAr8wmy`xpOj)Jj7)5EI-&v#AvkpTnY?1V!wzLLEUt8+U zARo!X4A(DYXkMv3Wl{535O%|c7~Z5ahMjcxdOnb$<$|la?rxPTu4+K>}aknrp990 zt_(qnCS7MG;|HWTo0~DW*K=!rdaCzXT9C$6x9Gy>&Qc6WSWge-0)2;NupHr;uW+(8aOw%w^~V7}dYM|q zJQ~jlFsPG!rf3nc_*`Kgx?(y4Pedf6<#EnmN^7ut#?w}bQWUfapeN#@{sfL@`ZGbVHFH?uRX%~Pho3wb@XL5r(DG*G8_;8X| z^nYir>4|U;a~^Hp@Mh^#+u4D-xs^FdJvQeDtHODRhjYSIq%H80!FD%A!1U;;pz(_o z$I{hO?GcQlusBR7s@h=>JWkhn(e0fJlI9Y(epoq&^R3=l^~;?U(x3WwSF4!?i$AE7j2CJ|vQ+wW5GcbtgQ8Zxs2Va0@ze&L-y>`x@Lm zp5ob`-RVcjbu`l>M&vVn0l+>Mb zZy*IwYiG_HPPuic!VOq3oldyO$-`+2sKj|LL&+#h^HwgJi!j~QynytMrGVSHnSB5T zbeL-jjAX?0=iJl$V%oN+i;KFw9WiyF(&OXiJj8W4%gNJKqe3d7?v|vE97XeBo4}+F z(u*2>Gu^}u$o_%$@ZfVQTI=l%_Jqf2U^e1K&gjgi$+Sc7&SRWqiVo z9^~%RDQzI<%%@|gGQuX(qjvDk6h0pbNp&Y>+1*{cl*RxFKopOvns268i~@L69lq8Q z0qnYqDMV|prgg#gtYZeLt)8E0JYqme3mNAy%{H832nU@}UGT?Xnr*b2pQE|$O0KoZ z7zV8=X*0jh<*#NQtX0rRydB__jBrl2xEOcPLDmx-=nlaYu@*& zVQ%c<#;E&Ae&u)X&Q;4*+Y_$X*n^e$97vZDh_Sq#HVMKGn1`SCL>|tlv2@>IegblL z<`%cBqA^6cyhp3rEoZ27*=EI z2(cd_I0XlDu7Nj|a*Pd}sEt&&H)m%_pwM7Mm}SH13|^7`3@c5ynp4z!GIfMEZROKw zHN5ZT1_!xrB1MC4x2s`!n7^C3u~iP#4@LFcdeyCltNF?2c0bofEvV;Y7!v>2P;P*> z3H0C3&wMY_5sCYc+vjmagtVL9gadnN%6q9BG#$t_lDf|4*lJZ8AP_D&nUnVi2_D;7Zf^rC`m^=IvTwEJMea{A11S!gGMARRU)Aw2^@6Qhx7+e}H0NDb z!s#H2Am~I_+No$KdnsRQ_U29@c+~S@h)VF2+i55m*+F^?4eCz%2w($0z@VWt@8xWZ z9e8^x1-0bnwm$1~f7LF~S1Xdqdpn}@uz~>AQkC{pkB6ea5)kiZ1;cATr-Xl-!pUn> z+)CpwMb?h0u_!rRIXajc&P9}s{5F_E^fNu~enf?xSuNJ97Q0dU<3Y-KhdBdCNLT5P zXhNOvKpk$$`79m-8g_eD zwK!Tpy){M5MAE?&gXwfTH@cIPj8{~j$Em%k)0sxcg@u)4LO~z5Qgi`&sXY(VA`7{e z?x{Vk??<^chi7Nb)p8TL3r53O)$L(uP>EWr5o}$@fxwL0PqCD~ySdynoR7@+b5?sU zM_%hsQM!sz0#XFW?xzA_t$yzckimGqi zJ8eIO-jCE2e12ir=u~dgUEPu+tEfpBzg9m6-g-KK1;#;+-)X%7{qrrFo<|XlPHIuJ zf<8!_W(?F*C_jKnRk^wUJD%i)e5W@viSOma!xX0ZhVr&2-pyaSCC!1RZug&va8vpJ zBrRt5&~3ewFX9!$J+UoKcB(X(^Ox(9vpYZ0 zLs=Fb`D}Mb(RdtDcuo6r;Yd~BdAj6L^=2s1dbmY;bIo$qo7RWosWo3{y9jPt&Mv7U zraG6IA&SKilV~%tY^HBO_6Wj@sgp4Z;b9QlP-hM>OQ_XP_W=LzrKZ!V zF=7k-o=X5rk>5@CjN}_bqcx>I&h0}QsaMi*yjjPsWcrw2oYBiE*;$QLO9xjM_-G~K zpuyRYyV7yiJ#=ci(r|7>A8pR9`H%{25n@4j9!f1^1LDU%4zf8MLEW4A2i`mG3rQ-m}?X+BT=(E)IuJMx#w$h~u$ zhjBkQx7F4s|Ie&``v)gK>hIQjGn{6lPstjhMU> zMmSsxL!3vscpu<gNn#Y(}ncwCiXUF}R;HLE|L7jIfu>SAat(g!}yp=U#}%V7=( zAm~aRDIumAuNl!ELY5=1g+OnJnWOXJw^~x z7o&iI(33_2a{>J$nKr3d@VXC;DH!Olf zkjrQ|eKBVi<>9hj=Q1&Ay)7Sx9G3W5Dqss@>8#~;7ZH*L%j&e*VrsDHWAEfOk;cd6xt}(>t^Pd!!zp4a zjf(JQ>A9CnF@@PQ9Kh_rs0)#}J#B?4&TgW9_eAL9blO-odCNY5DtVYzjQ83XV%zwV?x7E&mEusi>w z`<-R1v;2l4cy~&(>M=IpS;G;;+MrtzMLKhZ?hC_eF*iro;_n1kF)R?Uf$(`yZch)H?9j+H>o^e7nqLnh(6;cC5ITG&$3o z^46OA7`7d`u24E*S$Lc5hv~F2TEDTxH*4%bY~yTcuf~6Gnn-QY8JUkX0T_y*!bQEC zzQ-N0^x#47Rp%Y15J>zVDEExMa!?{?sG(6PoXyRLBNC)@I>#yVvnVa_pf>@O{8`A79gx?4#HC1tf(S^?USR770BKUxdCohG}LUk%#r zoH>~mC_5ts{J{%N%jzk%6sQ=ELfm_)0@dSAs$mFH)`v0dR;nhUNL@tY;rv*%H?W@J zr0FcSOS#})Y5_1qfa5$ENvj-1Z2BR6mH{`M(b82-Xe2i?2MEP}+)T$zrcEr!mpMB& z0%z;pTneN_IbrLZMn*`+WLgGpu@FJlQUq0XAfGWMF;aF?FyUCxTnR8O3<9{0T?5B# zEN4?zaI{t9AT4RaA{7dZ2*V181lH`*sT2k~Y>lkNBLfKT&c$3oP@J8JsyBh&N7ZPt zMYpB-kE_*jKE)dMENQ}NPl^deWh0#`#ze>mwpDO75nga+q(kNd1w;gqC6>X!t5yX( z{FM1>>qS|D(b5l5-Wl*hbSUhdxysrA@w4jVDevV)S2)k@S!zixB=I4 zJ2D=oFeb=ej$qgFve^>{QY$++-KIB9Ou$0ehL-WJZRdY+HZXo(sD+ejzW`Qs=aVg~ zXhH26`->?%v!GrTf1WyUT_%7zEy3q#x6*t94Uw3TcHz{^c_VRIo~`F#1XFK1=Z)m( zPHG7s5IMA*{)P`(1%Q&)nd97aECOj&pjX|ZgEK;{)Uy_2BrSZI!3aUirRUohCUZLs zqMp=}a62iqHGi!Sm?st+>z>gM8x+e4faAU8a45JmsA?fqxXAxu7*Y;VkqnuU)Q+__ zna`8CIfYCF9JZi+PgmJg`y$*~e%Y&9<0zjH`U+l3zabobaV$q+p;)JEV2%@HGAwj- z+TdS@kqmR#Amlq^dhk6i+= zb2GRX#RTWd)`Ul6Qh}=%QU-PoT>-Mk$9r4(V13A*2^Qh~*mo$RW+T(iuFa^%@ zGkyFZ{e&fGOM-U-*6i~eRehrme1d?0nT`}|zgGbi{;gcY$g<{RA+_b3SOV)kryD;P z({?j|DJ};19+x0iqlk;h<>RF#aSTz5U&EQHb5^LDi7mDQG@jH&KlT-UGrl z=WIYFmzbrA?hN3AAkYUWr+wAnOSi&*U>MX0x(ec9VVlv|0L3;Irm|R7Ad@Z0X3~{U zbecja5NQDmd{~uJ^vRQm#j&`Owqx}Wt}EjArSY{!7h)c`@dYr=CkV-8G+~UZT6cB7^m8c?AMugVFETj(_ZROf zG1cixzi;PeFf;2WN2twwIk%pPATSZ_6&1Xmp9WJfMG_wvNuO!en$L`eu}n_DEHAuP znoT{=)3CyQY}&?*ejTgoI395@Oldb%G58NK0!XozYk7wdfCW@gbTj_Dax;U2Gp(#@ z&^rw*ev;w@G^4w4^#Rd1HARsk$N;EEA_ON?33;U1@R0h* z?$o)I4YEcFqCaa>SRsE=BCJR_p2nGr0M>o6udqIQ^ihfu`UdB>T(J8%M=2FE5t?D71TNF%-1uzn3X+Iz5E+U_Q5{`F3*p-3ZcN z2`6qCVt1Y)xtF5XBg|<8<1u-h>jicRUSKy+Vv&STQdhJO6=5Iqjdn!%M-hx711qp3 z)kOoF_smT5;3zL~KrW_qp!i^>psvEMR#J$F{gZswa){mADHK~vj~8-{g-ColR!dP$ z0TCD*^Esar3d1&Yp`M?qRqfSHiWdvIPq_vyaOoRzQVIcpDqoR5PRzc);L(ON|0J$(XIGw;p z9XVIH``uiJ7zK1^?}_&>EjE@W?#@~85j+L>&~jeu`F>ip9M;=xJzv33Qz?#5!<^hp zZ_)V#XCeK=MX8(!;PgJb{!-2W$k;0`Qa3e+Ztg`ifn8QSL}8&b&_75xg>^l@o=4=9 z2u5vdSiSz~=Ekot4A>uSY`g0D#gmUd+xY5R6Sankx!EgECpLx|) z+q;LZKmId~lX03FU*=xP7ee>eKs=S@i22)^dgwFjQD+*;_OX`Z7K^zY1C@ba>_d8$ zZc=nN-aiQDc+R8Kpv07OEW)9squ@ZLW>P$Z=WaxKoEwzgNbDlxjtXVrEc9gR57ikh z0OSq~r%o^CR-O5U%iUBGO*^0Iv6}xWN%rjd2nqo;;xlM$kJLiYo?rt(X9$VrCrk!| zhIu3ffr7LXgkHoryk9?q650EGIgXcs$Pc4@n5&?bc6&+^vdcg!(iY*gT61l>o?8e$ z5b*&v=j!53Bi&psHdsWy-%nRtmDy_SQdA3&$ZF1+sD^|%$>WHOvI8cj|8uoJOhv$j zml00~w&V)54nW#c?M))I0OU}K)--AU{gqGuY~!ugz)34P1q-8as4(KhwyK#4Vax&t zj#BV^L|n`fHoX^Gj4FUeWF*lgD>-TzZjZRy$+okSYpi?JR$fwIJqsZdv7S2G!^JmJ zh4@+RUG^}P^s&?pwq-lvIGRggbTl+2`<9OL0;P!r(*jgeom}`dT8wkvOG*b;BBu=v zI1D3}X#ozcHyIy!zFm&xbpa`q)|^ z6j*p0`8JzR2w?hMXYc`{_wjbwx8%X-&Gk@UyMQ1kF1yZL$w~&fXUx^}h4xZjt@$ka zmyVAYCtjQ_W9HANWiTDERElu@sS~?qAxAoMUSH1R2hjaRtCrdU6wikmqI-)ix8>p)?CR{E|Fha>MV^;bpQ~dPKX(S z^e`QkQ#dyENp539E}{KGn@|=Ax0sqs#dDNvDmXQ(!QVpOJxWs*(}}kc8d-Qa7(aSf z^Y2$R(!oNmZac4=ze`Kj3Wb; z8NN)NT1+h&Q-I^O+^B?Q^W1l)SuNX#mDIF8%}2eG$YwO7`#~kpQEtVLN8vOPiGqiH zn)bxdgZc=DMad9HUQ+Ymqn^}r!0amc|^u55T*16`$hPrB$K-c1Fog?1gqfZ9tFs8lgTcL0)(8_bJ0pJ$ z!--oQ&bE@<+8&BOie4^moR&J2jxrM1xa%oe^biO7U%jI!`$35 ztme!q`a#)ATOyilZMcm?>4M7~$BLK&CkoI!jdaba-DNcj^cz;gdg?t=31^p=@J!7) zLm-|fITr>Xnhe&30t$SX%TNT)n3!}W1}dKy&7mXTbSB8213Gj9O=;m=rxpCH-H_J` z-|#RuXzX%{#!8m%=1Voz1GP=g)_)SOa%M<7{FcN!7a3&GLmL()@$P6TWARy18lLf#IX}RU^Ei84{^`*|- zHkY}Jb(UrvI?+sSXj}ENL4u0=(r1)0T_P;ew3PA6j%|Jm;RKvx>apaAU_wo$nUUU0 z2n){$Q&sTiVM>}y`4n5E1PMX*bDyLq(^%Fj<^y6`a|%67p^tMLY)qsAjwV6pOhEY6 zaYlvM5m<(o*4wFWgg_zoaxOQO4~@5wkc>!My2)-vVBXKAgu0lQ0x56`F6LdKH9zMP zb8nln6)QbYN&EcK@Q&Qp?ZmKG}dujvC>o6H5pv(=gUB9%D7Sg<2<3_v4rllA>X zid#%^r}@o`2Z&=$-!eepKohwM0^x%k=}+x3S^CoK>_z9UrHncYXE8V8K4C5lRid!_ zVRx2LFKXpPh8Wd98&_d6xw~6hG&wAy1{}iNP(S_eTfwuBBOw1 zba4bn5E1M}#6d9|xsTZ`bmKD2O31=)3M;V{)HPc*0LdbQ6~TWqn^x*fo18|dtyBBkjY#(u~|-1BT?Dt3S~)-{Rg4K#`eGE8;ePJ`%a_GW_-a%@Qd~Smu z)c-75)<4Tl@CmoDQHwTXsIX%st^}OYYZr4;Q$jgYDDVybMNCe0&Apkcp!c-p!U#HONeOj(yr!WN*@&PsdJ-c?T}2}RWmY2&&o&#_6l+e+A-HmXVLUD7h~OH% ziJy<$r~Pxo@c;=glRMQg0*QXU&vT|u-bQWH8Gqm+@*A1Q$k^2Z6Pf@*%{ zAnMH3xGhK{CKkE@Vg~nvAPVkT|MQJOT`8wU2jDAL7{6_VhXZj4G}yTW~>bXkCzq=vO9RO^ST z!rVfqyql8?PX}IcnBu4uKndFr><6~dnVaBWG0&m$Ry#1wenhh|nO^uHa1j9slPSSu z7Us8|k}gv@*ztVM1zlJtIcHYcKei6dVXc$<(9#RD;w%#+W~~~@ImiZgBOI-c!wr75 zlVZ^;Dd$$w{#->kK)2X7TNcI;J(5-d?%;?w#~3@&|H0>oum@CqsKxeV&XV02BhkbNZLE$G@Uts+R4~NCF)OMyd(B4j10<^ zBBnB%^Y3r7+W4qZEm;K87vh3Wr^&UR>8i=kaw|))09vJ4E!Xo|cUh)nCd|4h!W@y( z334Y|9C0y`jNy0UhB5M(jO;gL3M@Z{L~qs5I!W9EOOF^d|KpT~7crXuG5R>!2xkWH zd-CgIdBy%EseKD}p+~1uTb3E0_Q8w?BVi(<(vHv5t~4ulBwacCKWkPB zF7!x2HLyRoU(Tt*hPhkUBCX*`NUdb(*_01bjFtncFQz0!39e|94+MJ|@l87(7O)ky zo_#o&GqGQiuuBhtQ|!$}Ig+dlSWGKsy;C_`BFuPhyAv^NLpCCt420i&V1UzZv0p^3 z!a#a*72(_zXaUAX-fKwIDASZT=6=6EhO1 ziUh{(>=Xtx3Fp%ya54A~M=N;B`oJAImm8zf^ZL-7hw_<&ZlM}8t*Hvw!MMhUfX)P$yo6zBiF079M^d5ijC;X;`|oJE8|8m9Vur zC(`KU{G87%tOMu;fHGDg?=N#oF;;tMuAW7Wg6Y7{xh;fLA(C;|pHw|e@3(zD&Jj9~ zI4&iQMgZnY5|%Ql^s!!MYLsATL|A=>xo%P6>EH{qkP?>r_%KsR3d6P(0}Bv}z#S=| zVl-z!3aoz*bIe2*4Jm|BSe-B>EgAd|lYu(`8wcH6BGHX}($9QAAY^X0^VGKF{+mr* zt^4+TNsG55raIA|EV@FDCaP{k&qJXzyR@t<9y+GDB@rSLPJ;|2_+=xK+SdRJ#(@cF zd^1PHC-0Q!?=QKU>#cS(Z@DkBw7P8HX27`WDF*)$HW_P+R@O0V# z_j5aj0nKM7C*q~jZUG6PX3Q-ZR{+zz84}zT&S$C9Sg)1U>mu_WGhcBZRT3)BsB0g7(;t*2GQvO@ z#2O)!;D}iqu*@A1V8{CZG=JpqwV80A8FX-LOX_+qHqqc-*xF|Bewwc^-fci2>BH0o zvIW`Ok4Vfcvj+%!QMFueZp{p_MbRv|J26e7|5-kHJ2{CUQ*0Ef(r^lqKT(XD?c_YS zm`|Y{shQzsH>X6{zHV|8E#*qKGCNU%65aw12Q~qI90&`l1UW}_NjRl`g~ma%-Fhxk zqq@6XJRbhSeniB*KS}36KaW#9QXRzwpqzjIn=uZi;}5c7L;0+aXE}D1n;~e)G|MQ_ z&M>m1F@b;KYP0$}Bd4+XAR|G)6R5zlj3%3l<(L(`pL#+~HgfiSZUw%yo13Np#)2dQ zr?@IaGGbWhOckJKNPuMNtHb(5*Kie=SSAb|v}iG^7!hI`&vHXR%ysGP~1t6C52 z#v0&QvQ&XFIPB2SncCnBD=1?Xb|UHnAM$Z38vPd>4s?HzMy2J_?)p+Q@r%NPxFDD) zQq{_w%-4%D3gyK67t3qxhzFsD(Fyc_A%Nei4r(W^XH7(%rD3oXd6g=2CnURIp_4| zS1lJy!1zaxSj-RLbl4`xiOE9N5mm%c8WIT!|5EQJo~3d0$GDSEH`VlKFga1Q$GRH9!L>rjd^QpPGPW3&{iCThFaw)As zI9RYOIAw9Rh|3X=->ob`6Z%^Othjc!=40ST)b|)bp!tN;L*x!~5>t`d)SKGF0s3;2 zg=*pD>EVbLG{E-j$zL!_dxV)xL8qxF^kdCB@C(p!0_tl)n0PepIbXxmnUFwPR@!sA z_`fh1cB-OTtlSNcBE(rlKswPPm>e8vVAz8cz$U`XHo$I1vY{sW#aaNS!t!b*m zTm~3l&CQvVlrG*+=J`$RVoPqu^+LPH4iTZrGr)ACrchO7wW0a*4rpvXF&mu}Oj?SH zLY!n6+)G_>k^rWbOq)?_8f-m3QzQVaeJKF32=GL0^l}bspe>>b#x&^6e-_R~&PG}k zOv<$*UzQv8MHGB)>IYX>)<4-ACUO~V1vJQ23VtuRANGSO2j7F_(iJF9mL-9+d|dd$ z{3iV8e5l^`l;V2Bxh`Y{X;y$X?lBf2+jHl=dStYFne7Y%@hC#0ta5O=r4)t}NhcER zSz?qal5J^9&Z^@1fMxN1b2rc>(R48CCQ>fT*oyEt-yp|WFaqA>A5B_CApxxKVH-<glf>NRt8uks;b_26n`TuRZCQ2T=h@#CR25k@L#n3`;=NpkcRiLugO3MkSqmjtg z$De7n6COAn0od{5sUiPPdj=a9Y~hM)3CJGxin^-#@wsoOej;Ch6Zl}_1ldH4TfQ&L zE_2;dZAF->Rk^D%x}@Bki!gY5LWhfnVpC>-9HcN}7C6PT{;U%KL@pR(`z+T}M+De{ z+bOBoZ@RTiIgcy=yt+96LU>vH{|I83*%V?C`N&cF!ARi0cO#D_ z!h==7M?2NXb#ya@$XkGJpC{0J(0kIi;d;%@GvCWnzkeXpdG#O7Ly!b?GKivUc zm7^6T&4%Lf;B*J$vX-E2tZQZr2&3Zv09e3*>S71+GY&m%Ap#7hyvqM$qn*hZz*{hTnM#lv_7}aFt;F6id#sd;@L#%* zloy+6I_{G0w1v6Qo#Pyk{23HDo6Ra((U34J#aVacs0ej1e8DVm_Jve}A0Y;GAWvCn$?Zgc??+I+ zG(=Th$9P3!GwqTnHJoqS$ z%U7Z>k&6Jl%ZN3Tz8=g`&MZ+>W(MXTx(zoJ?85?O7z^2|!l{Adq{B=zrdcQ$TuemD zxLprm0`nIu!Pqt=Eux|%G_PGl^b0q&4Y@hL+2tf*@ozQv07J}11WKEXL1EQ+JSk+8 zc?_@6=jSO-V2u%dgKU+|UQ7}RG1LzXSxmEm6plq{CG|GO*}tx(5m<`w25i4PMX)v*e0Wo8LP4Fd3+@Pf}h}8;>4V*^{5{Aa-8)9fDojDtjv}SV#fK@h-1y6a=@d^tVda``KsE^{O1=5_ zi^xSdHl`pJ4=Iy3f?8+I4gM=dMoA$7a^~abEH`CgO~yTvM=wD>36t)UmX7@GlygzM)>i`P2qj65mvMk8n3}abnfMvUG#_ ze4;hTN%^7)8do>5#C;lM#CyjSMMTvMB&q@PB$TpT+Q%0HK_NX19rgOKNY7I>v{@~|t{fqB<&8wfh=09!vZyM_%jR{%W$Iqau2W`=yF#p5Nr=|Fxf_&={VSl?u#lC6>@xmYRx~o$1K%4p)?*W(=uTfw;5pz89LcTD@>v8s%}GKQ&Rh$-_&38*l$Cu$+&tbPP2eO~wnaqzp!uA) znDf!!;Ayr+Ic82Hw#^RyR@$In?2N2cXO(nzOiKRDh$>@+8R!9RxyvO~Mr}o)WKw zRl+NaQ!lzAy0h#Th95MDw~QCW&G3IEn&8OPu+tH5J;E@{%l12zV?MLDj&nVe7RA+q zEo_qpZP1B2Z%#LfK;jhVRS^Jv2|GvB$-n36`Xy$(%yBi*T*wGFP!2Ve<$wz!fQftS zJZFKbapKAY^VkH%t;_o2YuHIyW|`z9tTKD76*#^F(*-_cngDB%aJU!DTSTq4)R4~* zE(A>|fvYASWCdUW+Qk>dh~rHX6(s_LKiU{UhT;mCN?BO-`rbeUdn6T)2|?X9Qx|dz zePtKGDYehJ1!gTD65CKc86F%mf>NxONGaSI#kWkULH@*&g}4r-YR;g2AjA@mU2Tm> zDl#M7sSG!Rt`O46ex6?BY%dm%JfTCmgyoATNKD47#Rty16LCD@qb(x<6It*BIL9U< zEV2+*?aKn<*%xv9!cHWN_;Qock_6>gkUWlnYJ*E;N>Vtd@|VBZXt6k#tL--w-=brg zO|G+Ko0dvvclljqzFcm6hr)>ubApkIBZ1{$h*358L(O{XmRv3d+)jp);AT7usIu)( zb_nimim8YpxfyWhAWp?Pw}$}o48p^F=1EC9WA%44z91BH`yZN7VCCGN+5u`TgN_j* zM4R)m&=djoY-+lo!bMMaX$S^k0O8V5)i}tYcsr#8f3eqm2XFq=^Qh+%;BS4q$=I`2iq=y9EF+N6i{DQjri}lqt9r zmIjj;cuG;mQ?``Y4kU-Q0*URju=uWdK@FbmP*iV>ccjxQoUk*kX#cUdQ1leJo6Df| z62+|M2Jn5F>uCz+!JsB0)at*>>iAFw2d&=#DU(2td6*$_%z75T$KiV0RJ{EXzZk7x#R;>U`)2CcZ&IB@J#@+g zD#2&vq_ZL;!wsbkHuFEro+Ft7LdW)yUDHO$8Jzci&ce;Z{^NS@%(+%v?lm9>bl_+gcl=bqG$?@FX5enS~I z$b{f!84Elh#x8sq!Re?i5sNduGq3ZAA2h(0Aae=+o-FHDT)_qGiuM$tugVfG2_i8y z8&^8Vz*4TpQLvT);SbYZsK{JUTPbHgBC-=PIawv*By{U(1mT$J_H+U}C8Ch#LMi58 z*Rb#hehSF5hy>hxjVo)R`rJhH@&NJe;c{^k3ACYavuF)oN%X8IZTTIOfi`bPm+v8T zatkFY1)Wq+4y7Mun%c={+Nay;4gsO_dE3L-@18m^o^PJkKrO%qvO54@G`*HXyX2Hd zMiNM(>-e(@;SPs3a01(QI8v_Xe+wm=r)Q$&c{+>v$&h34@aw7}b$&G88Gwi|l4P>;Mc`Tu zWgeSNLza{_b{iiQ(5X3X}B~2Ei!!B)MGXmx)ra5TaM;e-G;q5nJI@C z0omkMv(?{;K+>!voEqJaB4H)NLSiw4iSW0i8ghYjNI30ChIaF#>ZVczp@eZn?&mi_ zHY_p1$^XPj!wn795!)oP1)t+yu5V52RQ^+mwuBBHr0LpI4^&ZcNuWaep^z^qy(P#t zV@VPJ!+cOM8->_3A1%qXV5)HyISQ!ZbU-@u_zc2vN9RfZg2t^PZS^w_y{ z9&m;gjRnG~#!F+DJd3E7rJI}beGemTo$Ko{KqGA(JF-_KvI zEa?K5D-9sw#B^k4QSn8aL^xf5|LN4va5QCFE8)bqqf^88WpKrEp3F_meciMiVK4Gm zelRespeyhRZ?6g47V(7JfrmVgF77ltmVN`H(xABkWmGlCp^@(N?8^v6VMBAYXo6Tq3)&r#`%73c9EB?M38A-n!PlUO^G^+ z&xT&;r9DuAc{Q=~B+d0OA#DYd8Orqq?LI02|$;Bn6-X-n~PUT~s{U4_oEJ>j49K&zaAiP3UnnS&Z`bjZ;N zP;?87p1Nc*MI|brUQ6V(8yYO0|pnTCYZ9hO+5hB+v|ZxM(~n*W-J)H9A4&dW08D>@N38D0l)t~I;TWw*m?FOUh?(37=Da!4 z|Bytv&_vhSw@{!sP|?9SzvN`7G}TH>#sxj_B*hnNQDRU|cFPx|g~!S>IcQ5%IhLSR z!mo5DLz1exP`XuiN=wgiZgUjLTGK>Fxhb!mxs91=+Zaq?=Dv(~x@#!^vkv*f03)c> z*dJy#ZIW+LXePiOZ^efDFy{kLwGoI67M$$lAS}DRob-yN_Z4E7M<mUN3QlH5%66&* zEI-S2reT4PF})J1f=;uOW;0Nh@?OqMVg!qhfnwk=(5CY3QCf*pl%NH#mKsmaQhbgA zAcGJkqfk;1*Zfa`;WoeN{_8cNZPCZ4xma$V1S%5N!lf4zR z(~$AEjUwByjUT4%K8BN_eBc9 zzpp;<+b@sbF!g_Fj>DJ9Zz!GaOL81Woq9R^hIPQ%OK}{&On$?9)X$gXILxZ~a`+9S z{}k`$f2iZ|W%3((I^Roj97f@OIsAroh{j8C9KK9`L+js5avY}i{DuwLE&u;eiGqK= zG%nH0L?!ZcrkBJeigomIP>F*7yfiM+%S0vO|9MGVqG$~-2bCz4NLDGB1!d&lpG)NS zF9VfGIPpv35=HEngG!WiqLBIja4ylyL?r?+yd*AB0GB_Y64kz@S_%GPt@aN;{J}fd z_dnLud_DfVP4BvH{jsLkKT~U3%ENVj<=d}6@Uf=<tR?=p6IVK{R4tz_EFr4Jkj+5Vw&>UO=gf%X=bcaBcEC0*nEvBB%MHX*yz*WtjsA$|r~f&2J^nBH6XMX24dX zX#;_t;$!INo*7@VYk&x`pRKyv`3&4+G$1j{%Ewc|izm6kQeHgn^6UVBz{zOYqICJJ zrB2{R?22froalFk3^i{P$iHs7Cn?n$`08_Jp~LaTFOT5FeogyKj@!pkGBSA zmg5&gfma{4C0ItR)`JSAvh<2iAsn=CQjS~j-ANAc5peE{fY4ZCCFB%B+Fz<}4R37E z5rJGBhwBj#6evDfRBgj+KK*=C<9qA1|MTg3wZF=#U5{{2)vKLB@Rd&OfQ(PqtNm3@ z?Rp0ENk;lp^J-^!e5F%6Im@4}SNp4++WP;gdbKmBzS60kM<703ul83twdL%7s$T6Z z6JP1nepThwe)6T?Y`XPK{qwI3(d~xk7Q)*rLUc<9R6MtOR_a%R=yt<%tG_}-w`@xl z&yB+WN)X*{cy2NHUlF2P_Qi_l2Jn3)h;BDLw-6p)5u#i4go@`@k5bhh)u8tKe2#eg zPd64W`4dYpTubzrS0EOS!?Oy8%P2tXf7-EdA$*`LqNBVbv2X}~0s^INekEezLU=8~ zaIa7-ocsak9AY?MiCDNiS+xYiy+W~YVnC~4xY!_7EZo15hC$XO{&DOgX2et!MqGKR zX+$t$Vhb-sjh4PZdj8h5Ur+2GDY{U_ajOuVz(QUg42q4wNb?d$H(#qGo-L;cWEHm% z7c)~CKxnB*9I~g$2f{Hjkuz}ROWczfu(rJBBP61?$2Q|3iugj7#i*9LLQIsW)QYnZ z{4t)-Ds>=#qC|@GISu7+It9vWgBy>9fe0sO9-9roi!oi&SYcrFq{fAFBp3_)E7@&f zZwhZ1O6?KDf$*Q%yP5{vB*M8^85(I6Gn1Nn%%n5*tNS7;RJJv zjmAO?;c=>}xyHpBBNHoMEWB6&2@1~h)?o5I$oV3#K5$HAn1>X~z9CP5;D^JE3`yE0 z0Fh(sJk^l=QzRG!1$9bp0nr;HsbC1-Bone;$jAa+5Oy!BLE;%{vZM_}X5o0ZQ|L`T z7~$Dy%qZPLQ$Vy}WeTtTfJ!j5Z@I zF|e>2dGipr>X8t)!) zT9h~*x`bdCDP32K>5|~|((_|PaEkjy+QbyYl@%t5Nq>;jMGq9JXUQspz$%j6Iv|?@ zR=U6*B=GVua)6BodLLD1YQ^4EQ-ttYO4gF{4Ph&W@TKYT##bYR-G3#w6D}x?TTj06 z$f83b@NZ1CDI~i%oA7ZHy zd;<6?JVMXSz#m3d!n{R=1#p(06;OUJVqm08MPcL%Q{_4k_ECtBfMnrOo<1gDh`0?I zBS2JENh@fOzw-R#v(!&!4Qs6u$fFoWgMeH)=jm6q6S{WukS1WGp7m%W%yVF+04M0Q0973VsqaWRRsQV#97f_s(X|nYJ%Ltw z?1D%mPe(ARM3YIsCL_*lMCiy3Oj_}HQV&Q^BQut0GMUKHNId2QJGX3T5}9E)TK8sC zR!dqc2iZ-P|xt4z>{$V5O`-$_FSGHftuJR_pSSv*Ru5uU|Y zJc?Mt(&s9nY_*b8@LI^2gR3e!!!vWl7h*DrOYs;p_cK8A{G9a^A@QUbGCaz~G}=~v zG`mY!hq!KwjW{#sNkLMcDrzq)*;NFKdM1b@kc2m0dA%efDNzD*9Xgx#&6=0o|jJaXiYTDC+QsB!> z41`rV$^V!+UbLW$e+irfCkVk6S7Ol>6TuX_lRgr_DZo<_(!Hwbz{V{(io$DQ5tECT zEmPcNgqILbC_hRl^1m6RdxVclTzs05VQ)<)o_b_xNioud6l@zaT6_#$MTn|EP8_ZdCFr3D7u}#{uFGZN((0VTPMb4lb znd#IF>c^vW*aJWEqt!AEz~akejyx0S0%>i# zB9`*R_DdHT50|if3O7fR{4GXsPt^v1Ny(}t50R)?mc{W1VhP4Ewl+*hQ0ZkPFO!sS zDrZW?LV+`#d5H*T*Q6DTJnYVSQl;;!KNh@aTk&v-FT*pI0KlFnkw2%J6f{`#r)e3p zcQI!R+QGTtdTLMOYhY1}JbX4Jg!kk8U!p7Jfe{cWs}|)(9M4ip*s533@i=Q}G;S^< zbC%OWz=r-s?HI4RgEz~1Nof&}OqZ4VOpT?Yuv55BmJdlLvni#M$?4)8W2E&bF;VA<}ze97Ed{0bO+4q)nV{Wwbs(70aEonP11I;33 zRlOZi1wkpPD^<%IYJ7GV#Y0|?aC0O#OL`5n!Ph}QY+e0i2i|!@8mLTv_1PGpZWbn z59sT{o#)$=rNO!<^2BZIocy0+A1}c;Zh8L zV_o2F33IctiZDFN`8fBs3FebnG0De02jscF&R_JC2Tb)q9B(Jc0tl$>n3qRCs0Z5c*8W#T+<{i^?1x;mmgpP1v0kEMKSmmGs) zN|d@)OlI9HNX71DS_sdl=p5w&Pe2egME#deib7Sg6p8*4^XJhrhq>;CZ6*SY2JbOs z^b;{f(3;iMTnMK}wThh*_Ldm=@_Yho)MXBW%|;{RV!jJ9E4g;)BVzxpRkl|V-&57? zO$Sk9Vm?+QIDWB*x7ZGhZF$awGp<%kk8xpw6=s6=4oBWJZp)?mS8+nD1>+`zLryrV zR-59JH zg9!krjNY5XKAP0fqh|@)QGat(^Yy0PJiQYjlq3z;pZF(@-_VqPy#DTAY-qcF?n{kl z^@i8V$v>M`7kF60X>IZ}EJ5=ng@`?rMkPtXgETjQgczRbN8_;f7$2|;IqdmbR?}Y1 zX$PS_=7~qu3ULFz6K+LoaLn4EnDw&KoTM?<({CsZ#-_}KCus;-^T#fKT!;g>U7Xh)>~+c3t82QjD^2#ZgJ|0t}I0J`dC33?rQj zsKp0Wy$XP7+ooHT{Rvi4)Iu~4a31EQJxS6ESD=A_eQs7?roc_~x477cskLxK6jTJsd&J#QM`VDMgS%m|2N~q(9@C+!)NZx}tI8s7QiTtaVDiCCcU?E4I9iQW$s& z8jOp!>$xsK6d-)SKp+rLZ>K1x@i6@d>tIi@U1=e1W! zqtg83OK-b%>sI|sP#Yt$_y;Ujf_ZV11S<;elX(HSa{NLiE+Vn~3zMm$`xQ;0EEbv& z;BY@LsL60-qfbkqVFQ%ks03U>mDU+1J!xmT`#@#zdMyZ!5yS~xTgz?W{t{aNWi*e4 z;v%1lE=~Kf;o*l#Xl=D(ZFmF-o!SVLwG{9Rd$Jx&43&fkb{P~YiX}~+c3?Dhr@dsF zD9M3bE|GOf{-Vu~B0Uj-P2$Q&b2I3Q9GEagt$t$(bmteN?<^%7?=!j1UgOaRNo>x_ zWk&C0%upC@$O=Fbqnx5!_2n#z5B$(fli5$+iKz%_-+>~PiD9hWk4V;Jp~D^kTNIy? z7jiyTno3#tc5QaqwR|CZ?FJ1Hd7DHCNAR f+CV$1F}qMY4E__gRyzGwB%XWnrT_R=!>#`p { 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( @@ -42,25 +49,35 @@ const useAccountStore = create((set, get) => ({ accountList: [], - selectedAccount: null, - - selectAccount: (account) => { - set(() => ({ - selectedAccount: account - })) - }, - disableAccount: async (accountId) => { const formData = new FormData() formData.append('wu_id', accountId) - formData.append('account_status', 'enable') + // 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) @@ -71,12 +88,11 @@ const useAccountStore = create((set, get) => ({ }, saveOrUpdateAccount: async (formValues) => { - const { selectedAccount } = get() const { userId } = usingStorage() const formData = new FormData() - formData.append('wu_id', selectedAccount.userId) - formData.append('lmi_sn', selectedAccount.lmi_sn) - formData.append('lmi2_sn', selectedAccount.lmi2_sn) + 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) @@ -110,7 +126,8 @@ const useAccountStore = create((set, get) => ({ lastLogin: r.wu_lastlogindate, travelAgency: r.travel_agency_name, travelAgencyId: r.travel_agency_id, - roleId: r.roles, + // 数据库支持逗号分隔多角色(5,6,7),目前界面只需单个。 + roleId: parseInt(r.roles), role: r.roles_name, } }) diff --git a/src/views/account/Management.jsx b/src/views/account/Management.jsx index f0e7b9f..6c2c323 100644 --- a/src/views/account/Management.jsx +++ b/src/views/account/Management.jsx @@ -12,92 +12,6 @@ import { PERM_ROLE_NEW } from '@/config' const { Title } = Typography -const permissionData = [ - { - title: '海外供应商', - value: 'oversea-0', - key: 'oversea-0', - children: [ - { - title: '所有海外功能', - value: 'oversea-0-0', - key: 'oversea-0-0', - }, - ], - }, - { - 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: 'B-1-0', - key: 'B-1-0', - }, - { - 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-01', - key: '2-1-01', - }, - { - title: '新增账号', - value: '2-1-11', - key: '2-1-11', - }, - { - title: '禁用账号', - value: '2-1-21', - key: '2-1-21', - }, - { - title: '重置账号密码', - value: '2-1-31', - key: '2-1-31', - }, - { - title: '新增角色', - value: '2-1-41', - key: '2-1-41', - }, - ], - }, -] - function Management() { const { t } = useTranslation() @@ -121,8 +35,7 @@ function Management() { }, { title: t('account:role'), - dataIndex: 'role', - render: roleRender + dataIndex: 'role' }, { title: t('account:lastLogin'), @@ -141,12 +54,6 @@ function Management() { ) } - function roleRender(text) { - return ( - - ) - } - function actionRender(text, account) { return ( @@ -156,28 +63,19 @@ function Management() { ) } - 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 [roleAllList, setRoleAllList] = useState([]) const [accountForm] = Form.useForm() - const [searchAccountByCriteria, accountList, disableAccount, selectedAccount, saveOrUpdateAccount, selectAccount] = + const [searchAccountByCriteria, accountList, disableAccount, saveOrUpdateAccount, resetAccountPassword] = useAccountStore((state) => - [state.searchAccountByCriteria, state.accountList, state.disableAccount, state.selectedAccount, state.saveOrUpdateAccount, state.selectAccount]) + [state.searchAccountByCriteria, state.accountList, state.disableAccount, state.saveOrUpdateAccount, state.resetAccountPassword]) const { notification, modal } = App.useApp() const onAccountSeleted = async (account) => { accountForm.setFieldsValue(account) - selectAccount(account) - console.info(account) const roleList = await fetchRoleList() setRoleAllList(roleList.map(r => { return { @@ -193,7 +91,6 @@ function Management() { console.log(values) saveOrUpdateAccount(values) .catch(ex => { - console.info(ex.message) notification.error({ message: 'Notification', description: ex.message, @@ -222,11 +119,21 @@ function Management() { } 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: ${account.username}, Realname: ${account.realname}`, onOk() { + resetAccountPassword(account.userId, randomPassword) + .then(() => { + notification.info({ + message: '新密码:' + randomPassword, + description: `请复制密码给 [${account.realname}]`, + placement: 'top', + duration: 60, + }) + }) console.log('ResetPassword') }, onCancel() { @@ -263,6 +170,9 @@ function Management() { )} > + + + onRoleSeleted(role)}>{text} - ) - } - - function actionRender(text, account) { - return ( - - - - - ) + function actionRender(text, role) { + if (role.role_id > 1) { + return ( + + ) + } } const onPermissionChange = (newValue) => { @@ -139,10 +131,14 @@ function RoleList() { } useEffect (() => { + setDataLoading(true) fetchRoleList() .then(r => { setRoleAllList(r) }) + .finally(() => { + setDataLoading(false) + }) }, []) const [permissionValue, setPermissionValue] = useState(['0-0-0']) @@ -151,16 +147,20 @@ function RoleList() { const [roleAllList, setRoleAllList] = useState([]) const [roleForm] = Form.useForm() - const [saveOrUpdateRole] = + const [saveOrUpdateRole, newRole] = useAccountStore((state) => - [state.saveOrUpdateRole]) + [state.saveOrUpdateRole, state.newRole]) const { notification, modal } = App.useApp() - const onRoleSeleted = async (role) => { + const onRoleSeleted = (role) => { + roleForm.setFieldsValue(role) + setRoleModalOpen(true) + } + + const onNewRole = () => { + const role = newRole() roleForm.setFieldsValue(role) - // selectAccount(account) - // console.info(account) setRoleModalOpen(true) } @@ -212,11 +212,7 @@ function RoleList() { )} > - - - + - + From ce345c73472cbf41d9118ce11ae75b9913da030e Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 24 Jun 2024 09:25:47 +0800 Subject: [PATCH 21/24] =?UTF-8?q?HT=E8=AF=AD=E7=A7=8D;=20=E5=BD=93?= =?UTF-8?q?=E5=89=8D=E8=AF=AD=E7=A7=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...seLanguageSets.js => useHTLanguageSets.js} | 2 +- src/hooks/useProductsSets.js | 67 ++++++++++++++----- src/i18n/LanguageSwitcher.jsx | 5 ++ 3 files changed, 56 insertions(+), 18 deletions(-) rename src/hooks/{useLanguageSets.js => useHTLanguageSets.js} (90%) diff --git a/src/hooks/useLanguageSets.js b/src/hooks/useHTLanguageSets.js similarity index 90% rename from src/hooks/useLanguageSets.js rename to src/hooks/useHTLanguageSets.js index 1dab4b1..73b827e 100644 --- a/src/hooks/useLanguageSets.js +++ b/src/hooks/useHTLanguageSets.js @@ -1,4 +1,4 @@ -export const useLanguageSets = () => { +export const useHTLanguageSets = () => { const newData = [ { key: '1', value: '1', label: 'English' }, { key: '2', value: '2', label: 'Chinese (中文)' }, diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 5f3a85b..35cd3f5 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -4,20 +4,39 @@ import { useTranslation } from 'react-i18next'; /** * 产品管理 相关的预设数据 * 项目类型 -1 酒店预定 -2 火车 -3 飞机票务 -4 游船 -5 快巴 -6 旅行社(综费) - -7 景点 - -8 特殊项目 - -9 其他 -A 酒店 -B 超公里 - -C 餐费 - -D 包价包 -X 站 +酒店预定 1 +火车 2 +飞机票务 3 +游船 4 +快巴 5 +旅行社(综费) 6 +景点 7 +特殊项目 8 +其他 9 +酒店 A +超公里 B +餐费 C +小包价 D +站 X +购物 S +餐 R +娱乐 E +精华线路 T +客人testimonial F +线路订单 O +省 P +信息 I +国家 G +城市 K +图片 H +地图 M +包价线路 L +节日节庆 V +火车站 N +手机租赁 Z + * * todo: webht 类型 +P 导游 +T 车费 */ export const useProductsTypes = () => { @@ -28,8 +47,8 @@ export const useProductsTypes = () => { const newData = [ { label: t('products:type.Experience'), value: '6', key: '6' }, { label: t('products:type.Overtravel'), value: 'B', key: 'B' }, - { label: t('products:type.Car'), value: 'Car', key: 'Car' }, - { label: t('products:type.Guide'), value: 'Guide', key: 'Guide' }, + { label: t('products:type.Car'), value: 'T', key: 'T' }, + { label: t('products:type.Guide'), value: 'P', key: 'P' }, { label: t('products:type.Package'), value: 'D', key: 'D' }, { label: t('products:type.Attractions'), value: '7', key: '7' }, { label: t('products:type.Meals'), value: 'C', key: 'C' }, @@ -42,7 +61,6 @@ export const useProductsTypes = () => { return types; }; - export const useProductsAuditStates = () => { const [types, setTypes] = useState([]); const { t, i18n } = useTranslation(); @@ -66,3 +84,18 @@ export const useProductsAuditStatesMapVal = (value) => { const stateMapVal = stateSets.reduce((r, c) => ({ ...r, [`${c.value}`]: c }), {}); return stateMapVal; }; + +export const useProductsTypesFieldsets = (type, role) => { + const infoDefault = ['code', 'title']; + const infoAdmin = ['remarks', 'dept']; + const infoTypesMap = { + '6': [], + 'B': ['city_id', 'km'], + '车费': ['description', 'city_id', 'recommends_rate', 'duration', 'display_to_c'], // todo: 包价类型?? + '导游': ['description', 'city_id', 'duration', ], + 'D': [], //todo: 木有图 + '7': ['description', 'city_id', 'recommends_rate', 'duration', 'display_to_c', 'open_weekdays'], // todo: 怎么是2个图 + 'C': ['description', 'city_id',], + '8': [], // todo: ? + }; +}; diff --git a/src/i18n/LanguageSwitcher.jsx b/src/i18n/LanguageSwitcher.jsx index af87005..dd1a743 100644 --- a/src/i18n/LanguageSwitcher.jsx +++ b/src/i18n/LanguageSwitcher.jsx @@ -8,6 +8,11 @@ const i18n_to_htcode = { 'en': 1, }; +export const useLanguage = () => { + const { i18n } = useTranslation(); + return { language: i18n.language, }; +}; + /** * 语言选择组件 */ From 219372c06e78eb50ce11aeb78de3206c96d8d12f Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 24 Jun 2024 10:58:14 +0800 Subject: [PATCH 22/24] =?UTF-8?q?=E6=96=B0=E5=A2=9EHT=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B:=20J=20=E8=BD=A6=E8=B4=B9,=20Q=20=E5=AF=BC?= =?UTF-8?q?=E6=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useProductsSets.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 35cd3f5..6749ede 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -34,9 +34,9 @@ import { useTranslation } from 'react-i18next'; 节日节庆 V 火车站 N 手机租赁 Z - * * todo: webht 类型 -P 导游 -T 车费 + * * webht 类型, 20240624 新增HT类型 +Q 导游 +J 车费 */ export const useProductsTypes = () => { @@ -47,8 +47,8 @@ export const useProductsTypes = () => { const newData = [ { label: t('products:type.Experience'), value: '6', key: '6' }, { label: t('products:type.Overtravel'), value: 'B', key: 'B' }, - { label: t('products:type.Car'), value: 'T', key: 'T' }, - { label: t('products:type.Guide'), value: 'P', key: 'P' }, + { label: t('products:type.Car'), value: 'J', key: 'J' }, + { label: t('products:type.Guide'), value: 'Q', key: 'Q' }, { label: t('products:type.Package'), value: 'D', key: 'D' }, { label: t('products:type.Attractions'), value: '7', key: '7' }, { label: t('products:type.Meals'), value: 'C', key: 'C' }, @@ -72,6 +72,7 @@ export const useProductsAuditStates = () => { { key: '2', value: '2', label: t('products:auditState.Approved') }, { key: '3', value: '3', label: t('products:auditState.Rejected') }, { key: '1', value: '1', label: t('products:auditState.Published') }, + // ELSE 未知 ]; setTypes(newData); }, [i18n.language]); @@ -85,15 +86,18 @@ export const useProductsAuditStatesMapVal = (value) => { return stateMapVal; }; +/** + * @ignore + */ export const useProductsTypesFieldsets = (type, role) => { const infoDefault = ['code', 'title']; - const infoAdmin = ['remarks', 'dept']; + const infoAdmin = ['remarks', 'dept', 'display_to_c']; const infoTypesMap = { '6': [], 'B': ['city_id', 'km'], - '车费': ['description', 'city_id', 'recommends_rate', 'duration', 'display_to_c'], // todo: 包价类型?? - '导游': ['description', 'city_id', 'duration', ], - 'D': [], //todo: 木有图 + 'J': ['description', 'city_id', 'recommends_rate', 'duration', 'display_to_c'], + 'Q': ['description', 'city_id', 'duration', ], + 'D': ['description', 'city_id', 'recommends_rate','duration',], '7': ['description', 'city_id', 'recommends_rate', 'duration', 'display_to_c', 'open_weekdays'], // todo: 怎么是2个图 'C': ['description', 'city_id',], '8': [], // todo: ? From 86242addab2776bbf2ec44555ccc07c6d435d610 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 24 Jun 2024 13:45:29 +0800 Subject: [PATCH 23/24] =?UTF-8?q?=E5=88=A0=E9=99=A4=20Mobx=20=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=9A=84=E5=BC=95=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.jsx | 10 - src/stores/Feedback.js | 1 - src/stores/Invoice.js | 368 ------------------------------------ src/views/invoice/Index.jsx | 4 +- 4 files changed, 1 insertion(+), 382 deletions(-) diff --git a/src/main.jsx b/src/main.jsx index d78954e..bd73f53 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,5 +1,4 @@ import React from "react"; -import { configure } from "mobx"; import ReactDOM from "react-dom/client"; import { createBrowserRouter, @@ -41,15 +40,6 @@ import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA, PERM_AIR_TICKET } import './i18n'; -configure({ - useProxies: "ifavailable", - enforceActions: "observed", - computedRequiresReaction: true, - observableRequiresReaction: false, - reactionRequiresObservable: true, - disableErrorBoundaries: process.env.NODE_ENV == "production" -}); - const router = createBrowserRouter([ { path: "/", diff --git a/src/stores/Feedback.js b/src/stores/Feedback.js index dd3feb1..9b2356e 100644 --- a/src/stores/Feedback.js +++ b/src/stores/Feedback.js @@ -1,4 +1,3 @@ -import { makeAutoObservable, runInAction } from 'mobx'; import { fetchJSON, postForm } from '@/utils/request'; import { groupBy } from '@/utils/commons'; import * as config from '@/config'; diff --git a/src/stores/Invoice.js b/src/stores/Invoice.js index 814e69f..09ac0f2 100644 --- a/src/stores/Invoice.js +++ b/src/stores/Invoice.js @@ -1,10 +1,5 @@ -import { makeAutoObservable, runInAction } from "mobx"; import { fetchJSON, postForm } from "@/utils/request"; -import { prepareUrl, isNotEmpty, objectMapper } from "@/utils/commons"; import { HT_HOST } from "@/config"; -import { json } from "react-router-dom"; -import * as config from "@/config"; -import dayjs from "dayjs"; import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; @@ -138,366 +133,3 @@ const useInvoiceStore = create( ); export default useInvoiceStore; - -export class Invoice { - constructor(root) { - makeAutoObservable(this, { rootStore: false }); - this.root = root; - } - - invoiceList = []; //账单列表 - invoicekImages = []; //图片列表 - invoiceGroupInfo = {}; //账单详细 - invoiceProductList = []; //账单细项 - invoiceZDDetail = []; //报账信息 - invoiceCurrencyList = []; //币种 - invoicePicList = []; //多账单图片列表数组 - invoiceFormData = { info_money: 0, info_Currency: "", info_date: "" }; //存储form数据 - - invoicePaid = [] ; //支付账单列表 - invoicePaidDetail = []; //每期账单详细 - - loading = false; - search_date_start = dayjs().subtract(2, "M").startOf("M"); - search_date_end = dayjs().endOf("M"); - - onDateRangeChange = dates => { - console.log(dates); - this.search_date_start = dates==null? null: dates[0]; - this.search_date_end = dates==null? null: dates[1]; - }; - - fetchInvoiceList(VEI_SN, GroupNo, DateStart, DateEnd,OrderType) { - this.loading = true; - const fetchUrl = prepareUrl(HT_HOST + "/service-cusservice/PTSearchGMBPageList") - .append("VEI_SN", VEI_SN) - .append("OrderType", 0) - .append("GroupNo", GroupNo.trim()) - .append("DateStart", DateStart) - .append("DateEnd", DateEnd) - .append("Orderbytype", 1) - .append("TimeType", 0) - .append("limitmarket", "") - .append("mddgroup", "") - .append("SecuryGroup", "") - .append("TotalNum", 0) - .append("PageSize", 2000) - .append("PageIndex", 1) - .append("PayState",OrderType) - .append("token",this.root.authStore.login.token) - .build(); - - return fetchJSON(fetchUrl).then(json => { - runInAction(() => { - this.loading = false; - if (json.errcode == 0) { - if (isNotEmpty(json.Result)) { - this.invoiceList = json.Result.map((data, index) => { - return { - key: data.GMDSN, - gmd_gri_sn: data.GMD_GRI_SN, - gmd_vei_sn: data.GMD_VEI_SN, - GetGDate: data.GetGDate, - GMD_FillWorkers_SN: data.GMD_FillWorkers_SN, - GMD_FWks_LastEditTime: data.GMD_FWks_LastEditTime, - GMD_VerifyUser_SN: data.GMD_VerifyUser_SN, - GMD_Dealed: data.GMD_Dealed, - GMD_VRequestVerify: data.GMD_VRequestVerify, - LeftGDate: data.LeftGDate, - GMD_FillWorkers_Name: data.GMD_FillWorkers_Name, - GroupName: data.GroupName, - AllMoney: data.AllMoney, - PersonNum: data.PersonNum, - GMD_Currency: data.GMD_Currency, - VName: data.VName, - FKState: data.FKState, - }; - }); - } else { - this.invoiceList = []; - } - } else { - throw new Error(json.errmsg + ": " + json.errcode); - } - }); - }); - } - - fetchInvoiceDetail(GMDSN, GSN) { - const fetchUrl = prepareUrl(HT_HOST + "/service-cusservice/PTGetZDDetail") - .append("VEI_SN", this.root.authStore.login.travelAgencyId) - .append("GRI_SN", GSN) - .append("GMD_SN", GMDSN) - .append("LGC", 1) - .append("Bill", 1) - .append("token",this.root.authStore.login.token) - .build(); - - return fetchJSON(fetchUrl).then(json => { - runInAction(() => { - if (json.errcode == 0) { - this.invoiceGroupInfo = json.GroupInfo[0]; - this.invoiceProductList = json.ProductList; - this.invoiceCurrencyList = json.CurrencyList; - this.invoiceZDDetail = json.ZDDetail; - } else { - throw new Error(json.errmsg + ": " + json.errcode); - } - }); - return json; - }); - } - - //获取供应商提交的图片 - getInvoicekImages(VEI_SN, GRI_SN) { - let url = `/service-fileServer/ListFile`; - url += `?GRI_SN=${GRI_SN}&VEI_SN=${VEI_SN}&FilePathName=invoice`; - url += `&token=${this.root.authStore.login.token}`; - fetch(config.HT_HOST + url) - .then(response => response.json()) - .then(json => { - console.log(json); - runInAction(() => { - this.invoicekImages = json.result.map((data, index) => { - return { - uid: -index, //用负数,防止添加删除的时候错误 - name: data.file_name, - status: "done", - url: data.file_url, - }; - }); - }); - }) - .catch(error => { - console.log("fetch data failed", error); - }); - } - - //从数据库获取图片列表 - getInvoicekImages_fromData(jsonData) { - let arrLen = jsonData.length; - let arrPicList = jsonData.map((data, index) => { - const GMD_Pic = data.GMD_Pic; - let picList = []; - if (isNotEmpty(GMD_Pic)) { - let js_Pic = JSON.parse(GMD_Pic); - picList = js_Pic.map((picData, pic_Index) => { - return { - uid: -pic_Index, //用负数,防止添加删除的时候错误 - name: "", - status: "done", - url: picData.url, - }; - }); - } - if (data.GMD_Dealed == false && arrLen == index + 1) { - this.invoicekImages = picList; - } - return picList; - }); - - runInAction(() => { - this.invoicePicList = arrPicList; - }); - } - - //获取数据库的表单默认数据回填。 - getFormData(jsonData) { - let arrLen = jsonData.length; - return jsonData.map((data, index) => { - if (data.GMD_Dealed == false && arrLen == index + 1) { - //只有最后一条账单未审核通过才显示 - runInAction(() => { - this.invoiceFormData = { info_money: data.GMD_Cost, info_Currency: data.GMD_Currency, info_date: isNotEmpty(data.GMD_PayDate) ? dayjs(data.GMD_PayDate) : "" }; - }); - } - }); - } - - removeFeedbackImages(fileurl) { - let url = `/service-fileServer/FileDelete`; - url += `?fileurl=${fileurl}`; - url += `&token=${this.root.authStore.login.token}`; - return fetch(config.HT_HOST + url) - .then(response => response.json()) - .then(json => { - console.log(json); - return json.Result; - }) - .catch(error => { - console.log("fetch data failed", error); - }); - } - - postEditInvoiceDetail(GMD_SN, Currency, Cost, PayDate, Pic, Memo) { - let postUrl = HT_HOST + "/service-cusservice/EditSupplierFK"; - let formData = new FormData(); - formData.append("LMI_SN", this.root.authStore.login.userId); - formData.append("GMD_SN", GMD_SN); - formData.append("Currency", Currency); - formData.append("Cost", Cost); - formData.append("PayDate", isNotEmpty(PayDate) ? PayDate : ""); - formData.append("Pic", Pic); - formData.append("Memo", Memo); - formData.append("token",this.root.authStore.login.token); - - return postForm(postUrl, formData).then(json => { - console.info(json); - return json; - }); - } - - postAddInvoice(GRI_SN, Currency, Cost, PayDate, Pic, Memo) { - let postUrl = HT_HOST + "/service-cusservice/AddSupplierFK"; - let formData = new FormData(); - formData.append("LMI_SN", this.root.authStore.login.userId); - formData.append("VEI_SN", this.root.authStore.login.travelAgencyId); - formData.append("GRI_SN", GRI_SN); - formData.append("Currency", Currency); - formData.append("Cost", Cost); - formData.append("PayDate", isNotEmpty(PayDate) ? PayDate : ""); - formData.append("Pic", Pic); - formData.append("Memo", Memo); - formData.append("token",this.root.authStore.login.token); - return postForm(postUrl, formData).then(json => { - console.info(json); - return json; - }); - } - - //账单状态 - invoiceStatus(FKState) { - switch (FKState - 1) { - case 1: - return "Submitted"; - break; - case 2: - return "Travel Advisor"; - break; - case 3: - return "Finance Dept"; - break; - case 4: - return "Paid"; - break; - default: - return ""; - break; - } - } - - fetchInvoicePaid(VEI_SN, GroupNo, DateStart, DateEnd) { - this.loading = true; - const fetchUrl = prepareUrl(HT_HOST + "/service-Cooperate/Cooperate/GetInvoicePaid") - .append("VEI_SN", VEI_SN) - .append("GroupNo", GroupNo) - .append("DateStart", DateStart) - .append("DateEnd", DateEnd) - .append("token",this.root.authStore.login.token) - .build(); - - return fetchJSON(fetchUrl).then(json => { - runInAction(() => { - this.loading = false; - if (json.errcode == 0) { - if (isNotEmpty(json.Result)) { - this.invoicePaid = json.Result.map((data, index) => { - return { - key: data.fl_id, - fl_finaceNo: data.fl_finaceNo, - fl_vei_sn: data.fl_vei_sn, - fl_year: data.fl_year, - fl_month: data.fl_month, - fl_memo: data.fl_memo, - fl_adddate: data.fl_adddate, - fl_addUserSn: data.fl_addUserSn, - fl_updateUserSn: data.fl_updateUserSn, - fl_updatetime: data.fl_updatetime, - fl_state: data.fl_state, - fl_paid: data.fl_paid, - fl_pic: data.fl_pic, - fcount: data.fcount, - pSum: data.pSum, - }; - }); - } else { - this.invoicePaid = []; - } - } else { - throw new Error(json.errmsg + ": " + json.errcode); - } - }); - }); - - } - - - fetchInvoicePaidDetail(VEI_SN,FLID){ - this.loading = true; - const fetchUrl = prepareUrl(HT_HOST + "/service-Cooperate/Cooperate/GetInvoicePaidDetail") - .append("VEI_SN", VEI_SN) - .append("fl_id", FLID) - .append("token",this.root.authStore.login.token) - .build(); - - return fetchJSON(fetchUrl).then(json => { - runInAction(() => { - this.loading = false; - if (json.errcode == 0) { - if (isNotEmpty(json.Result)) { - this.invoicePaidDetail = json.Result.map((data, index) => { - return { - key: data.fl2_id, - fl2_fl_id: data.fl2_fl_id, - fl2_GroupName: data.fl2_GroupName, - fl2_gri_sn: data.fl2_gri_sn, - fl2_gmd_sn: data.fl2_gmd_sn, - fl2_wl: data.fl2_wl, - fl2_ArriveDate: data.fl2_ArriveDate, - fl2_price: data.fl2_price, - fl2_state: data.fl2_state, - fl2_updatetime: data.fl2_updatetime, - fl2_updateUserSn: data.fl2_updateUserSn, - fl2_memo: data.fl2_memo, - fl2_memo2: data.fl2_memo2, - fl2_paid: data.fl2_paid, - fl2_pic: data.fl2_pic, - }; - }); - } else { - this.invoicePaidDetail = []; - } - } else { - throw new Error(json.errmsg + ": " + json.errcode); - } - }); - }); - } - - /* 测试数据 */ - //账单列表范例数据 - testData = [ - { - GSMSN: 449865, - gmd_gri_sn: 334233, - gmd_vei_sn: 628, - GetDate: "2023-04-2 00:33:33", - GMD_FillWorkers_SN: 8617, - GMD_FWks_LastEditTime: "2023-04-26 12:33:33", - GMD_VerifyUser_SN: 8928, - GMD_Dealed: 1, - GMD_VRequestVerify: 1, - TotalCount: 22, - LeftGDate: "2023-03-30 00:00:00", - GMD_FillWorkers_Name: "", - GroupName: " 中华游230501-CA230402033", - AllMoney: 3539, - FKState: 1, - GMD_Currency: "", - PersonNum: "1大1小", - VName: "", - }, - ]; -} - -// export default Invoice; diff --git a/src/views/invoice/Index.jsx b/src/views/invoice/Index.jsx index 17a9603..559a718 100644 --- a/src/views/invoice/Index.jsx +++ b/src/views/invoice/Index.jsx @@ -1,6 +1,4 @@ import { NavLink, useNavigate } from "react-router-dom"; -import { useState } from "react"; -import { toJS } from "mobx"; import { Row, Col, Space, Button, Table, App, Steps } from "antd"; import { formatDate, isNotEmpty } from "@/utils/commons"; import { AuditOutlined, SmileOutlined, SolutionOutlined, EditOutlined } from "@ant-design/icons"; @@ -103,7 +101,7 @@ function Index() { -
+
From 697fa00be361584dcbe0ad3926b5828a0935e779 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 24 Jun 2024 14:58:14 +0800 Subject: [PATCH 24/24] =?UTF-8?q?=E4=BA=A7=E5=93=81=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=9A=84=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/config.js b/src/config.js index fc69f82..03d67fd 100644 --- a/src/config.js +++ b/src/config.js @@ -31,3 +31,10 @@ export const PERM_DOMESTIC = '/domestic/all' // 机票供应商 // category: air-ticket export const PERM_AIR_TICKET = '/air-ticket/all' + +// 价格管理 +export const PERM_PRODUCTS_MANAGEMENT = '/products/*'; // 管理 +export const PERM_PRODUCTS_INFO_AUDIT = '/products/info/audit'; // 信息.审核 +export const PERM_PRODUCTS_INFO_PUT = '/products/info/put'; // 信息.录入 +export const PERM_PRODUCTS_OFFER_AUDIT = '/products/offer/audit'; // 价格.审核 +export const PERM_PRODUCTS_OFFER_PUT = '/products/offer/put'; // 价格.录入