diff --git a/config/proxy.ts b/config/proxy.ts index 585353b..8d4f66d 100644 --- a/config/proxy.ts +++ b/config/proxy.ts @@ -1,10 +1,11 @@ const proxy = { '/auth': { - target: 'http://home.robot-z.cn:7001', + // target: 'http://home.robot-z.cn:7001', + target: 'https://es.robot-z.cn', changeOrigin: true, secure: false, pathRewrite: { '^/auth': '' }, - onProxyRes: function(proxyRes) { + onProxyRes: function (proxyRes) { proxyRes.headers['Access-Control-Allow-Origin'] = '*'; proxyRes.headers['Access-Control-Allow-Methods'] = 'GET,PUT,POST,DELETE,OPTIONS'; proxyRes.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'; diff --git a/config/router.ts b/config/router.ts index 65dae3e..1f28796 100644 --- a/config/router.ts +++ b/config/router.ts @@ -28,17 +28,22 @@ export default [ { path: '/examine/index', name: '考评分类管理', - component: "@/pages/examine/index", + component: "@/pages/examine/index/index", }, { path: '/examine/question', name: '考核问题管理', - component: "@/pages/examine/question", + component: "@/pages/examine/question/index", }, { path: '/examine/modal', name: '考核模版管理', - component: "@/pages/examine/modal", + component: "@/pages/examine/modal/index", + }, + { + path: '/examine/record', + name: '考核记录管理', + component: "@/pages/examine/record/index", } ] diff --git a/package.json b/package.json index 25a6392..531aa0f 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "crypto-js": "^4.2.0", "dva": "^3.0.0-alpha.1", "moment": "^2.30.1", + "qrcode": "^1.5.4", "umi": "^4.3.24", "xlsx": "^0.18.5", "xlsx-style-fixed": "^0.0.4" diff --git a/src/components/leftSelectTree.tsx b/src/components/leftSelectTree/leftSelectTree.tsx similarity index 97% rename from src/components/leftSelectTree.tsx rename to src/components/leftSelectTree/leftSelectTree.tsx index 01e0f73..151f4c6 100644 --- a/src/components/leftSelectTree.tsx +++ b/src/components/leftSelectTree/leftSelectTree.tsx @@ -8,6 +8,7 @@ import ProForm, { ProFormText } from "@ant-design/pro-form"; import { Button, Col, FormInstance, Row, Tree } from "antd"; // import { getServerpartTree } from "@/services/options"; import useRequest from "@ahooksjs/use-request"; +import { getMerchantShopTree, getServerpartTree } from "./service"; // import './style.less' // import { getMerchantShopTree } from "@/pages/Setting/Users/service"; @@ -26,6 +27,9 @@ type DetailProps = { noWj?: any // 把万佳商贸隐藏 } const LeftSelectTree = ({ setSelectedId, reload, actionRef, currentUser, width, otherFun, setCollapsible, collapsible, haveTest, handleGetLeftTreeData, noWj }: DetailProps) => { + + console.log('currentUser', currentUser); + const searchTreeRef = useRef(); // 默认的服务区树 const [allTreeViews, setAllTreeViews] = useState() @@ -37,7 +41,7 @@ const LeftSelectTree = ({ setSelectedId, reload, actionRef, currentUser, width, if (currentUser?.UserPattern === 2000) { data = await getMerchantShopTree({ BusinessManId: currentUser?.BusinessManID, ShowShop: false }); } else { - data = await getServerpartTree(currentUser?.ProvinceCode, currentUser?.CityAuthority, true, true, true) + data = await getServerpartTree(currentUser?.provinceCode, currentUser?.CityAuthority, true, true, true, false, 1000) } console.log('datatree', data); @@ -233,5 +237,5 @@ const LeftSelectTree = ({ setSelectedId, reload, actionRef, currentUser, width, } export default connect(({ user }: ConnectState) => ({ - currentUser: user.currentUser, -}))(LeftSelectTree); + currentUser: user.data +}))(LeftSelectTree); \ No newline at end of file diff --git a/src/components/leftSelectTree/service.ts b/src/components/leftSelectTree/service.ts new file mode 100644 index 0000000..8401fce --- /dev/null +++ b/src/components/leftSelectTree/service.ts @@ -0,0 +1,61 @@ +import request from "@/utils/requestOld" + +export async function getServerpartTree(ProvinceCode?: number | string, ServerpartCodes?: string, + ShowWholePower?: boolean, ShowSPRegion?: boolean, ShowRoyalty?: boolean | false, ShowCompactCount?: boolean | false, StatisticsType?: number): Promise { + // ShowRoyalty 无论传入什么都是false + const data = await request(`/BaseInfo/GetServerpartTree? + ProvinceCode=${ProvinceCode || ''}&ServerpartCodes=${ServerpartCodes || ''}& + ShowWholePower=${ShowWholePower || false}&ShowWholePower=${ShowSPRegion || true}& + ShowRoyalty=${false}&ShowCompactCount=${ShowCompactCount || false}&StatisticsType=${StatisticsType || ''}`, { + method: 'GET', + }); + if (data.Result_Code !== 100) { + return []; + } + + + return data.Result_Data.List; +} + + +export async function getMerchantShopTree(params: { BusinessManId?: any, provinceCode?: number }) { + const data = await request(`/FrameWork/GetMerchantShopTree`, { + method: 'GET', + params + }) + + if (data.Result_Code !== 100) { + return [] + } + + const treeTable = wrapTreeNode(data.Result_Data.List); + return [...treeTable]; +} + +export function wrapTreeNode(data: any[]) { + + const wrapData: any = data.map((item: any) => { + const node = { ...item.node }; + + if (item.children && item.children.length > 0) { + node.children = wrapTreeNode(item.children); + } + return node + }); + return wrapData; +} + + +export async function handleGetServerpartDDL(params: any) { + const data = await request(`/BaseInfo/GetServerpartDDL`, { + method: 'GET', + params + }) + + if (data.Result_Code !== 100) { + return [] + } + + return data.Result_Data.List +} + diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index dd96b41..914fa92 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -193,6 +193,13 @@ const BasicLayout: FC<{ user: UserModelState, global: ProfileModelState, dispatc hideInMenu: false, name: "考核模版管理", path: "/examine/modal", + }, + { + SYSTEMMODULE_DESC: "", + guid: "5", + hideInMenu: false, + name: "考核记录管理", + path: "/examine/record", } ] diff --git a/src/models/user.ts b/src/models/user.ts index 7cda16a..68e1937 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -235,6 +235,11 @@ const UserModel: UserModelType = { path: '/examine/modal', name: '考核模版管理', component: "@/pages/examine/modal", + }, + { + path: '/examine/record', + name: '考核记录管理', + component: "@/pages/examine/record", } ] @@ -269,6 +274,7 @@ const UserModel: UserModelType = { '/examine/index', '/examine/modal', '/examine/question', + '/examine/record', ], rootSubmenuKeys: handleGetRootSubmenuKeys(menuRes.data), indexAllMenuItemById: handleGetEachDatumFromNestedDataByKey(menuRes.data, 'id'), diff --git a/src/pages/authority.ts b/src/pages/authority.ts index 3b6b2ab..dfbde5d 100644 --- a/src/pages/authority.ts +++ b/src/pages/authority.ts @@ -11,6 +11,9 @@ const authority: PageAuthority = { '/examine/question': [ '/examine/question', ], + '/examine/record': [ + '/examine/record', + ], }; diff --git a/src/pages/examine/index.tsx b/src/pages/examine/index/index.tsx similarity index 51% rename from src/pages/examine/index.tsx rename to src/pages/examine/index/index.tsx index 1b20b09..37a6968 100644 --- a/src/pages/examine/index.tsx +++ b/src/pages/examine/index/index.tsx @@ -1,20 +1,17 @@ import { connect } from "umi"; -import { useRef, useState } from "react"; -import { Button, message, Modal, Space, type FormInstance } from "antd"; +import { Children, useRef, useState } from "react"; +import { Button, message, Modal, Popconfirm, Space, type FormInstance } from "antd"; import type { ActionType } from "@ant-design/pro-table"; import ProTable from "@ant-design/pro-table"; import moment from "moment"; -import LeftSelectTree from "@/components/leftSelectTree"; -import { handleGetAddExamineType, handleGetExamineTypeList } from "./service"; -import { ProForm, ProFormSwitch, ProFormText, ProFormTextArea } from "@ant-design/pro-components"; +import LeftSelectTree from "@/components/leftSelectTree/leftSelectTree"; +import { handleGetAddExamineType, handleGetDeleteExamineType, handleGetEditExamineType, handleGetExamineTypeList, handleGetExamineTypeTreeList } from "./service"; +import { ProForm, ProFormSwitch, ProFormText, ProFormTextArea, ProFormTreeSelect } from "@ant-design/pro-components"; const examineIndex: React.FC<{ currentUser: any }> = (props) => { - console.log('props', props); const { currentUser } = props - console.log('currentUser', currentUser); - const actionRef = useRef(); const formRef = useRef(); // 弹出框的表单实例 @@ -23,12 +20,15 @@ const examineIndex: React.FC<{ currentUser: any }> = (props) => { const [searchParams, setSearchParams] = useState() // 打开新增的悬浮框 const [openAddModal, setOpenAddModal] = useState(false) + // 当前行数据 + const [currentRow, setCurrentRow] = useState() const columns: any = [ { title:
分类名称
, dataIndex: "name", align: 'left', + ellipsis: true, width: 200, hideInSearch: true }, @@ -36,12 +36,14 @@ const examineIndex: React.FC<{ currentUser: any }> = (props) => { title:
分类注释
, dataIndex: "description", align: 'left', + ellipsis: true, width: 250, hideInSearch: true }, { title: "有效状态", dataIndex: "status", + ellipsis: true, align: 'center', width: 150, hideInSearch: true, @@ -52,6 +54,7 @@ const examineIndex: React.FC<{ currentUser: any }> = (props) => { { title: "创建时间", dataIndex: "createdAt", + ellipsis: true, align: 'center', width: 200, hideInSearch: true, @@ -62,6 +65,7 @@ const examineIndex: React.FC<{ currentUser: any }> = (props) => { { title: "更新时间", dataIndex: "updatedAt", + ellipsis: true, align: 'center', width: 200, hideInSearch: true, @@ -77,14 +81,48 @@ const examineIndex: React.FC<{ currentUser: any }> = (props) => { width: 150, render: (_: any, record: any) => { return - 编辑 - 删除 + { + console.log('record', record); + setCurrentRow(record) + setOpenAddModal(true) + }}>编辑 + { + console.log('record', record); + handleDeleteType(record.id) + }} + > + 删除 + + } } ] + // 删除分类 + const handleDeleteType = async (id: number) => { + const data = await handleGetDeleteExamineType(id) + if (data.code === 200) { + message.success(data.message) + actionRef.current?.reload() + } + } + // 若children没有值 则变为null + const handleGetNoChildren = (list: any) => { + if (list && list.length > 0) { + list.forEach((item) => { + if (item.children && item.children.length > 0) { + item.children = handleGetNoChildren(item.children) + } else { + item.children = null + } + }) + } + return list + } return (
@@ -103,12 +141,18 @@ const examineIndex: React.FC<{ currentUser: any }> = (props) => { expandable={{ expandRowByClick: true }} + rowKey={(record) => { + return `${record?.id}` + }} + scroll={{ y: 'calc(100vh - 400px)' }} headerTitle={考评分类管理} search={{ span: 6 }} request={async (params) => { - const data = await handleGetExamineTypeList() + const req = {} + let data = await handleGetExamineTypeTreeList(req) console.log('table', data); if (data && data.length > 0) { + data = handleGetNoChildren(data) return { data, success: true } } return { data: [], success: true } @@ -116,6 +160,7 @@ const examineIndex: React.FC<{ currentUser: any }> = (props) => { toolbar={{ actions: [
{ modalRef.current?.validateFields().then(async (res) => { - console.log('res', res); - const req = { - name: res.name, - description: res.description, - sortOrder: "1", - status: res.status, - operator: currentUser?.operator + let req = {} + let data = {} + if (currentRow?.id) { + req = { + ...currentRow, + parentId: res.parentId, + name: res.name, + description: res.description, + status: res.status, + operator: currentUser?.operator + } + data = await handleGetEditExamineType(req) + } else { + req = { + parentId: res.parentId, + name: res.name, + description: res.description, + sortOrder: "1", + status: res.status, + operator: currentUser?.operator + } + data = await handleGetAddExamineType(req) } - const data = await handleGetAddExamineType(req) + console.log('req', req); console.log('data', data); if (data.code === 200) { + modalRef.current?.resetFields() message.success(data.message) setOpenAddModal(false) actionRef.current?.reload() + setCurrentRow(undefined) } else { message.error(data.message) } }) }} onCancel={() => { + modalRef.current?.setFieldsValue({}) + setCurrentRow(undefined) setOpenAddModal(false) }} > - { - console.log('values', values); - }} - > + { + console.log('currentRow', currentRow); + return { ...currentRow } + }}> + { + const req = {} + const data = await handleGetExamineTypeList(req) + console.log('dsadas', data); + let res = [ + { + name: '/', + id: 0, + children: data + } + ] + return res + }} + fieldProps={{ + treeDefaultExpandedKeys: [0], + fieldNames: { + label: "name", + value: "id" + }, + }} + rules={[ + { required: true, message: '请选择类别层级!' } + ]} + /> + = (props) => { - - ) diff --git a/src/pages/examine/index/service.ts b/src/pages/examine/index/service.ts new file mode 100644 index 0000000..1634c41 --- /dev/null +++ b/src/pages/examine/index/service.ts @@ -0,0 +1,37 @@ +import request from "@/utils/request" + +// 拿到类别列表接口 +export async function handleGetExamineTypeList(params?: any) { + const data = await request.get('/question-categories', params) + if (data.code === 200) { + return data.data + } + return [] +} + +// 拿树形的 +export async function handleGetExamineTypeTreeList(params?: any) { + const data = await request.get('/question-categories/tree', params) + if (data.code === 200) { + return data.data + } + return [] +} + +// 新增类别列表接口 +export async function handleGetAddExamineType(params: any) { + const data = await request.post('/question-categories', params) + return data +} + +// 删除类别接口 +export async function handleGetDeleteExamineType(params: any) { + const data = await request.get(`/question-categories/delete/${params}`) + return data +} + +// 编辑分类接口 +export async function handleGetEditExamineType(params: any) { + const data = await request.post(`/question-categories/${params.id}`, params) + return data +} \ No newline at end of file diff --git a/src/pages/examine/modal.tsx b/src/pages/examine/modal.tsx deleted file mode 100644 index d3497a0..0000000 --- a/src/pages/examine/modal.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { UserConnectedProps } from "@/models/user"; -import { connect } from "umi"; - - -type DetailProps = { - -} -const modal = ({ }: DetailProps) => { - - return ( -
- modal -
- ) -} - -export default connect( - ({ user }: { user: UserConnectedProps['user'] }) => ({ - user - }), -)(modal); diff --git a/src/pages/examine/modal/index.tsx b/src/pages/examine/modal/index.tsx new file mode 100644 index 0000000..c440b64 --- /dev/null +++ b/src/pages/examine/modal/index.tsx @@ -0,0 +1,553 @@ +import LeftSelectTree from "@/components/leftSelectTree/leftSelectTree"; +import { handleGetServerpartDDL } from "@/components/leftSelectTree/service"; +import { ActionType, FormInstance, ProCard, ProForm, ProFormList, ProFormSelect, ProFormSwitch, ProFormText, ProFormTextArea, ProTable } from "@ant-design/pro-components"; +import { Button, Col, message, Modal, Popconfirm, Row, Space } from "antd"; +import moment from "moment"; +import { useRef, useState } from "react"; +import { connect } from "umi"; +import { handleAddTemplates, handleDeleteTemplates, handleGetQuestionList, handleGetTemplatesList, handleUpdateTemplates, handleUploadFile } from "./service"; +import { handleGetExamineTypeTreeList } from "../index/service"; +import QRCode from 'qrcode'; +import { base64ToFile } from "@/utils/publicMethods"; + +const examineModal: React.FC<{ currentUser: any }> = (props) => { + const { currentUser } = props + + const actionRef = useRef(); + const formRef = useRef(); + const modalActionRef = useRef(); + const modalFormRef = useRef(); + + + + // 弹出框的表单实例 + const modalRef = useRef() + // 树相关的属性和方法 + const [selectedId, setSelectedId] = useState() + const [collapsible, setCollapsible] = useState(false) + + // 显示新增点位的悬浮框 + const [showPlaceModal, setShowPlaceModal] = useState(false) + // 当前点击选中的问题行 + const [currentRow, setCurrentRow] = useState() + // 服务区的枚举 + const [serviceObj, setServiceObj] = useState() + // 生成的二维码的初始 + const [qrCodeUrl, setQrCodeUrl] = useState() + // 选择问题的悬浮框 + const [selectQuestionModal, setSelectQuestionModal] = useState(false) + // 选择的问题列表 详情 + const [selectedQuestionDetail, setSelectedQuestionDetail] = useState() + // 选择的问题列表的id + const [selectedQuestionId, setSelectedQuestionId] = useState() + + + const columns: any = [ + { + title:
服务区名称
, + dataIndex: "serverPartName", + hideInSearch: true, + width: 200, + ellipsis: true + }, + { + title:
点位名称
, + dataIndex: "title", + hideInSearch: true, + width: 200, + ellipsis: true + }, + { + title:
有效状态
, + dataIndex: "status", + hideInSearch: true, + align: 'center', + width: 100, + ellipsis: true, + render: (_, record) => { + return record?.status ? '有效' : '无效' + } + }, + { + title:
更新时间
, + dataIndex: "updatedAt", + hideInSearch: true, + align: 'center', + width: 150, + ellipsis: true, + render: (_, record) => { + return record?.updatedAt ? moment(record?.updatedAt).format('YYYY-MM-DD HH:mm:ss') : '' + } + }, + { + title:
备注说明
, + dataIndex: "description", + hideInSearch: true, + width: 200, + ellipsis: true + }, + { + title:
问题列表
, + dataIndex: "questions", + hideInSearch: true, + width: 200, + ellipsis: true, + render: (_, record) => { + return record?.questions && record?.questions.length > 0 ? JSON.stringify(record?.questions) : '' + } + }, + { + title: '操作', + dataIndex: 'option', + align: 'center', + hideInSearch: true, + width: 150, + render: (_: any, record: any) => { + return + { + console.log('record', record); + setCurrentRow(record) + setShowPlaceModal(true) + }}>编辑 + { + deleteQuestion(record?.id) + }} + > + 删除 + + + + } + } + ] + + // 弹出框里面的 + const modalColumns: any = [ + { + title: "问题分类", + dataIndex: "id", + hideInTable: true, + valueType: 'treeSelect', + request: async () => { + const data = await handleGetExamineTypeTreeList() + console.log('data', data); + return data && data.length > 0 ? data.map(item => ({ + title: item.name, + value: item.id, + children: item.children?.map((child: any) => ({ + title: child.name, + value: child.id, + children: child.children || [] + })) || [] + })) : [] + }, + fieldProps: { + multiple: false, + treeDefaultExpandAll: true, + showSearch: true, + treeNodeFilterProp: 'title', + placeholder: '请选择问题分类' + } + }, + { + title: "考核内容", + dataIndex: "title", + width: 200, + ellipsis: true, + hideInSearch: true + }, + { + title: "考核选项", + dataIndex: "options", + width: 500, + ellipsis: true, + hideInSearch: true, + render: (_, record) => { + return record?.options && record?.options.length > 0 ? JSON.stringify(record?.options) : "" + } + } + ] + + // 删除模版 + const deleteQuestion = async (id: number) => { + const data = await handleDeleteTemplates({ id: id }) + if (data.code === 200) { + message.success(data.message) + actionRef.current?.reload() + } + } + + return ( +
+ {/* */} + +
+ 走动式点位管理} + search={{ span: 6 }} + request={async () => { + const req: any = { + + } + const data = await handleGetTemplatesList() + if (data && data.length > 0) { + return { data, success: true } + } + return { data: [], success: true } + }} + toolbar={{ + actions: [ + + ] + }} + > + + + { + modalRef.current?.validateFields().then(async (res) => { + + console.log('res', res); + console.log('currentRow', currentRow); + + let req = {} + let data = {} + if (currentRow?.id) { + let questions: any = [] + if (selectedQuestionDetail && selectedQuestionDetail.length > 0) { + selectedQuestionDetail.forEach((item) => { + questions.push({ + questionId: item.id, + isRequired: item.required, + sortOrder: item.sortOrder + }) + }) + } else { + // 判断是否有改动 一样 说明没改动 那么把老的拿进去就行 + if (currentRow?.questionnaireTemplateQuestions.length === res.questions.length) { + if (currentRow?.questionnaireTemplateQuestions && currentRow?.questionnaireTemplateQuestions.length > 0) { + currentRow?.questionnaireTemplateQuestions.forEach((item) => { + questions.push({ + questionId: item.question.id, + isRequired: item.question.required, + sortOrder: item.question.sortOrder + }) + }) + } + } else { + // 不一样 说明有删除 不然走的是第一个 + if (res.questions && res.questions.length > 0) { + res.questions.forEach((item) => { + questions.push({ + questionId: item.id, + isRequired: item.required, + sortOrder: item.sortOrder + }) + }) + } + } + } + + req = { + ...currentRow, + title: res.title, + serverPartName: serviceObj ? serviceObj[res.serverPartId] : "", + serverPartId: res.serverPartId, + questions: questions && questions.length > 0 ? questions : "", + operator: currentUser?.operator, + status: res.status, + description: res.description + } + data = await handleUpdateTemplates(req) + } else { + let questions: any = [] + if (selectedQuestionDetail && selectedQuestionDetail.length > 0) { + selectedQuestionDetail.forEach((item) => { + questions.push({ + questionId: item.id, + isRequired: item.required, + sortOrder: item.sortOrder + }) + }) + } + + req = { + title: res.title, + placeName: "", + placeId: 0, + serverPartName: serviceObj ? serviceObj[res.serverPartId] : "", + serverPartId: res.serverPartId, + // qrUrl: "", + questions: questions && questions.length > 0 ? questions : "", + operator: currentUser?.operator, + status: res.status, + description: res.description + } + data = await handleAddTemplates(req) + console.log('datadsadsa1', data.data); + const myQRCodeDataUrl = await QRCode.toDataURL(`pages/walkAroundManager/index?id=${data.data.id}`); + const file = base64ToFile(myQRCodeDataUrl, `wenjuan${data.data.id}.png`); + console.log('file', file); + const formData = new FormData(); + formData.append("file", file, `wenjuan${data.data.id}.png`); // 确保文件名也传递 + + const fileData = await handleUploadFile(formData) + console.log('fileData', fileData); + let imgUrl: string = `https://es.robot-z.cn/${fileData.data.path}` + await handleUpdateTemplates({ + ...data.data, + qrUrl: imgUrl + }) + setQrCodeUrl(imgUrl) + // setQrCodeUrl(myQRCodeDataUrl) + } + console.log('datadsadsa', data); + if (data.code === 200) { + // modalRef.current?.resetFields() + message.success(data.message) + // setShowPlaceModal(false) + actionRef.current?.reload() + // setCurrentRow(undefined) + } else { + message.error(data.message) + } + + }) + }} + onCancel={() => { + modalRef.current?.resetFields() + setShowPlaceModal(false) + setCurrentRow(undefined) + setQrCodeUrl(undefined) + }} + > + { + console.log('currentRow', currentRow); + + let questionsList: any = [] + let keyList: any = [] + if (currentRow?.questionnaireTemplateQuestions && currentRow?.questionnaireTemplateQuestions.length > 0) { + console.log('1'); + + currentRow?.questionnaireTemplateQuestions.forEach((item) => { + console.log('2'); + if (item.question) { + let obj = JSON.parse(JSON.stringify(item.question)) + obj.text = obj.title + obj.mark = JSON.stringify(obj.options) + keyList.push(obj.id) + questionsList.push(obj) + } + }) + } + + // setSelectedQuestionDetail + setSelectedQuestionId(keyList) + return { + ...currentRow, + serverPartId: currentRow.serverPartId.toString(), + questions: questionsList + } + }}> + + + { + const req = { + ProvinceCode: currentUser?.provinceCode, + StatisticsType: 1000 + } + const data = await handleGetServerpartDDL(req) + let obj: any = {} + if (data && data.length > 0) { + data.forEach((item) => { + obj[item.value] = item.label + }) + } + setServiceObj(obj) + return data + }} + fieldProps={{ + showSearch: true, + }} + rules={[{ + required: true, + message: "请选择服务区!" + }]} + /> + + + + + + + + + + { + console.log('e', e); + } + }} + copyIconProps={false} + style={{ width: '100%' }} + itemContainerStyle={{ width: '100%' }} + itemRender={({ listDom, action }, { record, index }) => ( +
+
{listDom}
+
{action}
+
+ )} + > + + + + + + + + +
+ + + + + + + + + + + + + { + qrCodeUrl || currentRow?.qrUrl ? + : "" + } + + + +
+
+
+ + { + console.log('selectedQuestionDetail', selectedQuestionDetail); + // 显示的问题列表 + let showQuestion: any = [] + if (selectedQuestionDetail && selectedQuestionDetail.length > 0) { + selectedQuestionDetail.forEach((item) => { + showQuestion.push({ text: item.title, mark: item.options ? JSON.stringify(item.options) : "" }) + }) + } + modalRef.current?.setFieldsValue({ + questions: showQuestion + }) + setSelectQuestionModal(false) + }} + onCancel={() => { + setSelectQuestionModal(false) + }} + > + { + console.log('查询参数:', params); + const req: any = { + categoryId: params.id + } + const data = await handleGetQuestionList(req) + if (data && data.length > 0) { + return { data, success: true } + } + return { data: [], success: true } + + }} + rowSelection={{ + type: "checkbox", + defaultSelectedRowKeys: selectedQuestionId, + onChange: (selectedRowKeys, selectedRows) => { + setSelectedQuestionDetail(selectedRows) + setSelectedQuestionId(selectedRowKeys) + } + }} + /> + +
+
+ ) +} + +export default connect(({ user }: ConnectState) => ({ + currentUser: user.data +}))(examineModal); + diff --git a/src/pages/examine/modal/service.ts b/src/pages/examine/modal/service.ts new file mode 100644 index 0000000..5f56f1c --- /dev/null +++ b/src/pages/examine/modal/service.ts @@ -0,0 +1,45 @@ +import requestOld from "@/utils/requestOld" +import request from "@/utils/request" + + +// 拿到模版id列表 去绑定服务区的多个站点信息 的列表接口 +export async function handleGetTemplatesList(params?: any) { + const data = await request.get('/questionnaire-templates', params) + if (data.code === 200) { + return data.data + } + return [] +} + +// 新建模版 +export async function handleAddTemplates(params?: any) { + const data = await request.post('/questionnaire-templates', params) + return data +} + +// 更新模版 +export async function handleUpdateTemplates(params?: any) { + const data = await request.post(`/questionnaire-templates/${params.id}`, params) + return data +} + +// 删除模版 +export async function handleDeleteTemplates(params?: any) { + const data = await request.get(`/questionnaire-templates/delete/${params.id}`) + return data +} + + +export async function handleGetQuestionList(params?: any) { + const data = await request.get(`/questions?categoryId=${params.categoryId}`,) + if (data.code === 200) { + return data.data + } + return [] +} + +// 上传图片的oss接口 +export async function handleUploadFile(params?: any) { + const data = await request.post(`/oss/upload`, params) + return data +} \ No newline at end of file diff --git a/src/pages/examine/question.tsx b/src/pages/examine/question.tsx deleted file mode 100644 index 7fbacef..0000000 --- a/src/pages/examine/question.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { UserConnectedProps } from "@/models/user"; -import { connect } from "umi"; - - -type DetailProps = { - -} -const question = ({ }: DetailProps) => { - - return ( -
- question -
- ) -} - -export default connect( - ({ user }: { user: UserConnectedProps['user'] }) => ({ - user - }), -)(question); diff --git a/src/pages/examine/question/index.tsx b/src/pages/examine/question/index.tsx new file mode 100644 index 0000000..29319b6 --- /dev/null +++ b/src/pages/examine/question/index.tsx @@ -0,0 +1,358 @@ + +import { ActionType, FormInstance, ProForm, ProFormDigit, ProFormList, ProFormRadio, ProFormSelect, ProFormSwitch, ProFormText, ProFormTextArea, ProFormTreeSelect, ProTable } from "@ant-design/pro-components"; +import { Button, Col, message, Modal, Popconfirm, Row, Space } from "antd"; +import { useRef, useState } from "react"; +import { connect } from "umi"; +import { handleAddQuestion, handleDeleteQuestion, handleEditQuestion, handleGetQuestionList } from "./service"; +import { handleGetExamineTypeList, handleGetExamineTypeTreeList } from "../index/service"; +import moment from "moment"; + +const examineQuestion: React.FC<{ currentUser: any }> = (props) => { + const { currentUser } = props + + const actionRef = useRef(); + const formRef = useRef(); + // 显示新增问题的悬浮框 + const [showQuestionModal, setShowQuestionModal] = useState(false) + // 当前点击选中的问题行 + const [currentRow, setCurrentRow] = useState() + // 弹出框的表单实例 + const modalRef = useRef() + // 问题分类的枚举 + const [categoryIdObj, setCategoryIdObj] = useState() + + const columns: any = [ + { + title:
问题分类
, + dataIndex: "description", + width: 200, + hideInSearch: true, + ellipsis: true + }, + { + title:
问题内容
, + dataIndex: "title", + width: 200, + hideInSearch: true, + ellipsis: true + }, + { + title:
问题类型
, + dataIndex: "type", + width: 200, + hideInSearch: true, + ellipsis: true, + render: (_, record) => { + let questionType: string = record?.type && record?.type.split('_') && record?.type.split('_').length > 0 ? record?.type.split('_')[0] : '' + return questionType === 'radio' ? '单选' : "多选" + } + }, + { + title:
必填
, + dataIndex: "required", + width: 100, + hideInSearch: true, + ellipsis: true, + render: (_, record) => { + return record?.required ? '是' : "否" + } + }, + { + title:
选项内容
, + dataIndex: "options", + width: 150, + hideInSearch: true, + ellipsis: true, + render: (_, record) => { + return record?.options && record?.options.length > 0 ? JSON.stringify(record?.options) : '' + } + }, + { + title:
更新时间
, + dataIndex: "updatedAt", + width: 150, + hideInSearch: true, + ellipsis: true, + render: (_, record) => { + return record?.updatedAt ? moment(record?.updatedAt).format('YYYY-MM-DD HH:mm:ss') : '' + } + }, + { + title:
操作人
, + dataIndex: "operator", + width: 150, + hideInSearch: true, + ellipsis: true + }, + { + title: '操作', + dataIndex: 'option', + align: 'center', + hideInSearch: true, + width: 150, + render: (_: any, record: any) => { + return + { + console.log('record', record); + setCurrentRow(record) + setShowQuestionModal(true) + }}>编辑 + { + deleteQuestion(record?.id) + }} + > + 删除 + + + + } + } + ] + + // 删除分类 + const deleteQuestion = async (id: number) => { + const data = await handleDeleteQuestion({ id: id }) + if (data.code === 200) { + message.success(data.message) + actionRef.current?.reload() + } + } + + return ( +
+
+ 考核问题管理} + search={{ span: 6 }} + request={async (params) => { + const data = await handleGetQuestionList() + console.log('data', data); + + if (data && data.length > 0) { + return { data, success: true } + } + return { data: [], success: true } + }} + toolbar={{ + actions: [ + + ] + }} + /> +
+ + + { + modalRef.current?.validateFields().then(async (res) => { + let req = {} + let data = {} + if (currentRow?.id) { + req = { + ...currentRow, + categoryId: res.categoryId,// 题目id + type: `${res.selectType === 1 ? 'radio' : 'checked'}_choice`,// 题目类型 + title: res.title,// 问题内容 + required: res.required,// 是否必填 + options: res.options, + status: res.status, + operator: currentUser?.operator, + description: `${res.categoryId ? categoryIdObj[res.categoryId] : ''}` + } + data = await handleEditQuestion(req) + } else { + req = { + categoryId: res.categoryId,// 题目id + type: `${res.selectType === 1 ? 'radio' : 'checked'}_choice`,// 题目类型 + title: res.title,// 问题内容 + required: res.required,// 是否必填 + options: res.options, + sortOrder: 1, + status: res.status, + operator: currentUser?.operator, + defaultScore: 0, + images: [], + description: `${res.categoryId ? categoryIdObj[res.categoryId] : ''}` + } + data = await handleAddQuestion(req) + } + console.log('datadsadsa', data); + if (data.code === 200) { + modalRef.current?.resetFields() + message.success(data.message) + setShowQuestionModal(false) + actionRef.current?.reload() + setCurrentRow(undefined) + } else { + message.error(data.message) + } + + }) + + }} + onCancel={() => { + modalRef.current?.resetFields() + setShowQuestionModal(false) + setCurrentRow(undefined) + }} + > + { + console.log('currentRow', currentRow); + return { + ...currentRow, + selectType: currentRow.type.split('_')[0] === 'checked' ? 2 : 1 + } + }}> + + + { + + + const req = {} + const data = await handleGetExamineTypeTreeList(req) + console.log('dsadas', data); + let res = [ + { + name: '/', + id: 0, + children: data + } + ] + let obj: any = {} + const labelList = await handleGetExamineTypeList() + if (labelList && labelList.length > 0) { + labelList.forEach((item) => { + obj[item.id] = item.name + }) + } + setCategoryIdObj(obj) + return res + }} + fieldProps={{ + treeDefaultExpandedKeys: [0], + fieldNames: { + label: "name", + value: "id" + }, + showSearch: true, + filterTreeNode: (input, treeNode) => { + return (treeNode?.name ?? '').toLowerCase().indexOf(input.toLowerCase()) >= 0; + }, + treeNodeFilterProp: 'name' + }} + /> + + + + + + + + + + + + + + + + ( +
+
{listDom}
+
{action}
+
+ )} + > + + + + + + + + +
+ +
+
+
+
+ ) +} + +export default connect(({ user }: ConnectState) => ({ + currentUser: user.data +}))(examineQuestion); diff --git a/src/pages/examine/question/service.ts b/src/pages/examine/question/service.ts new file mode 100644 index 0000000..b8d91e7 --- /dev/null +++ b/src/pages/examine/question/service.ts @@ -0,0 +1,30 @@ +import request from "@/utils/request" + +// 拿到问题列表接口 +export async function handleGetQuestionList(params?: any) { + const data = await request.get('/questions', params) + if (data.code === 200) { + return data.data + } + return [] +} + +// 新增问题接口 +export async function handleAddQuestion(params?: any) { + const data = await request.post('/questions', params) + return data +} + +// 编辑问题接口 +export async function handleEditQuestion(params?: any) { + const data = await request.post(`/questions/${params.id}`, params) + return data +} + +// 删除问题 +export async function handleDeleteQuestion(params?: any) { + const data = await request.get(`/questions/delete/${params.id}`) + return data +} + + diff --git a/src/pages/examine/record/index.tsx b/src/pages/examine/record/index.tsx new file mode 100644 index 0000000..73fa8c5 --- /dev/null +++ b/src/pages/examine/record/index.tsx @@ -0,0 +1,165 @@ +import { ConnectState } from "@/models/global"; +import { ActionType, FormInstance, ProTable } from "@ant-design/pro-components"; +import { useRef, useState } from "react"; +import { connect } from "umi"; +import { handleGetRecordList } from "./service"; +import moment from "moment"; +import { Button, Image } from "antd"; + +const examineRecord: React.FC<{ currentUser: any }> = (props) => { + const { currentUser } = props + + const actionRef = useRef(); + const formRef = useRef(); + // 显示的附件数据 + const [showImgList, setShowImgList] = useState([]) + // 预览图片 + const [imagePreviewVisible, setImagePreviewVisible] = useState(false) + // 预览的索引 + const [previewIndex, setPreviewIndex] = useState(0) + + const columns: any = [ + { + title:
服务区名称
, + dataIndex: "serverPartName", + hideInSearch: true, + width: 150, + ellipsis: true, + render: (_, record) => { + return record?.template.serverPartName ? record?.template.serverPartName : "-" + } + }, + { + title:
站点名称
, + dataIndex: "placeName", + hideInSearch: true, + width: 150, + ellipsis: true, + render: (_, record) => { + return record?.template.title ? record?.template.title : "-" + } + }, + { + title:
巡查内容
, + dataIndex: "uploadResult", + hideInSearch: true, + width: 200, + ellipsis: true, + render: (_, record) => { + let extendObj = record?.extend ? JSON.parse(record?.extend) : "" + return extendObj?.uploadResult ? extendObj?.uploadResult : "-" + } + }, + { + title:
巡查结果
, + dataIndex: "uploadResult", + hideInSearch: true, + width: 350, + ellipsis: true, + render: (_, record) => { + let obj: any = {} + if (record?.questionResponses && record?.questionResponses.length > 0) { + record?.questionResponses.forEach((item) => { + obj[item.question.title] = JSON.stringify(item.choiceResponse) + }) + } + return obj ? JSON.stringify(obj) : "-" + } + }, + { + title:
巡查时间
, + dataIndex: "createdAt", + hideInSearch: true, + width: 150, + ellipsis: true, + align: 'center', + render: (_, record) => { + return record?.createdAt ? moment(record?.createdAt).format('YYYY-MM-DD HH:mm:ss') : '-' + } + }, + { + title:
现场图片
, + dataIndex: "placeName", + hideInSearch: true, + width: 150, + ellipsis: true, + align: 'center', + render: (_, record) => { + let extendObj = record?.extend ? JSON.parse(record?.extend) : "" + let imgList = extendObj.imgsList + return imgList && imgList.length > 0 ? + : "-" + } + } + ] + + return ( +
+
+ 考核记录管理} + search={{ span: 6 }} + request={async () => { + const req: any = { + + } + const data = await handleGetRecordList() + console.log('data', data); + + if (data && data.length > 0) { + return { data, success: true } + } + return { data: [], success: true } + }} + toolbar={{ + + }} + > + +
+ + { + showImgList && showImgList.length > 0 &&
+ + { + setImagePreviewVisible(vis) + }, + current: previewIndex + }}> + { + showImgList.map((n) => + + ) + } + + + +
+ } +
+ ) +} + +export default connect(({ user }: ConnectState) => ({ + currentUser: user.data +}))(examineRecord); \ No newline at end of file diff --git a/src/pages/examine/record/service.ts b/src/pages/examine/record/service.ts new file mode 100644 index 0000000..4b20aa9 --- /dev/null +++ b/src/pages/examine/record/service.ts @@ -0,0 +1,13 @@ +import request from "@/utils/request" + +// 拿到问题列表接口 +export async function handleGetRecordList(params?: any) { + const data = await request.get('/questionnaire-responses', params) + if (data.code === 200) { + return data.data + } + return [] +} + + + diff --git a/src/pages/examine/service.ts b/src/pages/examine/service.ts deleted file mode 100644 index fe5f26c..0000000 --- a/src/pages/examine/service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import request from "@/utils/request" - -// 拿到类别列表接口 -export async function handleGetExamineTypeList(params?: any) { - const data = await request.get('/question-categories', params) - if (data.code === 200) { - return data.data - } - return [] -} - -// 新增类别列表接口 -export async function handleGetAddExamineType(params: any) { - const data = await request.post('/question-categories', params) - return data -} \ No newline at end of file diff --git a/src/pages/standard/index.tsx b/src/pages/standard/index.tsx index eee1d39..0913e0e 100644 --- a/src/pages/standard/index.tsx +++ b/src/pages/standard/index.tsx @@ -8,7 +8,7 @@ import * as XLSX from 'xlsx'; // 读写数据的核心工具(无需写样式 import XLSXStyle from 'xlsx-style-fixed'; import moment from "moment"; import { handleFormatNumber } from "@/utils/publicMethods"; -import LeftSelectTree from "@/components/leftSelectTree"; +import LeftSelectTree from "@/components/leftSelectTree/leftSelectTree"; import { UserConnectedProps } from "@/models/user"; diff --git a/src/utils/publicMethods.ts b/src/utils/publicMethods.ts index df778d6..c7a03a4 100644 --- a/src/utils/publicMethods.ts +++ b/src/utils/publicMethods.ts @@ -22,3 +22,16 @@ export const handleFormatNumber = (num: any) => { return `${integer}.${decimal}`; }; + +// base64转为文件格式 +export const base64ToFile = (base64Data: any, filename = "qrcode.png") => { + const arr = base64Data.split(","); + const mime = arr[0].match(/:(.*?);/)[1]; // 提取 MIME 类型 + const bstr = atob(arr[1]); // 解码 Base64 + let n = bstr.length; + const u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); + } + return new File([u8arr], filename, { type: mime }); +} diff --git a/src/utils/request.ts b/src/utils/request.ts index e60fe9f..c020c8f 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -19,10 +19,12 @@ instance.interceptors.request.use( // config.data = preprocessData(JSON.stringify(config.data)); // 调用预处理函数 // } + const isUpload = config.url?.includes("/oss/upload"); + config.headers = { ...config.headers, Authorization: `Bearer ${localStorage.getItem('Authorization') || ''}`, - "Content-Type": "application/json;charset=utf-8" + "Content-Type": isUpload ? "multipart/form-data" : "application/json;charset=utf-8", } as AxiosRequestHeaders; return config; diff --git a/src/utils/requestOld.ts b/src/utils/requestOld.ts new file mode 100644 index 0000000..4d3cb56 --- /dev/null +++ b/src/utils/requestOld.ts @@ -0,0 +1,151 @@ +import axios from 'axios'; +import { getDvaApp } from 'umi'; +import { notification } from 'antd'; +import type { AxiosRequestHeaders } from 'axios/index'; +import CryptoJS from "crypto-js"; + +const { UMI_APP_BASEURL } = process.env; + +// const instance = axios.create({ baseURL: UMI_APP_BASEURL }); +const instance = axios.create({ baseURL: 'https://api.eshangtech.com/EShangApiMain' }); +// const instance = axios.create({ baseURL: '/auth' }); + + + +instance.interceptors.request.use( + (config) => { + // 对data数据进行加密 + // if (config.data) { + // config.data = preprocessData(JSON.stringify(config.data)); // 调用预处理函数 + // } + + config.headers = { + ...config.headers, + Authorization: `Bearer ${localStorage.getItem('Authorization') || ''}`, + "Content-Type": "application/json;charset=utf-8" + } as AxiosRequestHeaders; + + return config; + }, + (error) => Promise.reject(error), +); + +instance.interceptors.response.use( + //状态码为2xx的时候执行 + (response) => { + const { data } = response; + + if (data.code !== 200 && data.Result_Code !== 100) { + notification.error({ + message: data.message, + }); + } + + const timestamp = getFormattedDate() + + return data + }, + //状态码不是2xx的时候执行 + (error) => { + const { response } = error; + + if (response && response.status === 401) { + // // 清除本地存储的token + // localStorage.removeItem('Authorization'); + // // 重定向到登录页 + // window.location.href = '/user/login'; + // notification.error({ + // message: response?.data?.message || '请求失败', + // description: error.message + // }); + } else { + notification.error({ + message: response?.data?.message || '请求失败', + description: error.message + }); + } + + return Promise.reject({ + code: response?.status || 500, + message: response?.data?.message || '请求失败' + }); + }, +); + + +// 加密 +const encryptAESECB = (data: string, key: string) => { + // const cipher = CryptoJS.createCipheriv('aes-128-ecb', key, null); // ECB 模式不需要 IV + const newKey = CryptoJS.enc.Utf8.parse(key); // 密钥必须是 16 字节 + const cipher = CryptoJS.AES.encrypt(data, newKey, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }); + let encrypted = cipher.ciphertext.toString(CryptoJS.enc.Hex); + // let encrypted = cipher.update(data, 'utf8', 'hex'); + // encrypted += cipher.final('hex'); + return encrypted; +} + +// 解密 +const decryptAESECB = (data: string, key: string) => { + // const decipher = CryptoJS.createDecipheriv('aes-128-ecb', key, null); + // let decrypted = decipher.update(data, 'hex', 'utf8'); + // decrypted += decipher.final('utf8'); + const newKey = CryptoJS.enc.Utf8.parse(key); + + const encryptedData = CryptoJS.enc.Hex.parse(data); + + // 解密操作 + const decrypted = CryptoJS.AES.decrypt({ ciphertext: encryptedData }, newKey, { + mode: CryptoJS.mode.ECB, // ECB 模式 + padding: CryptoJS.pad.Pkcs7 // PKCS7 填充方式 + }); + // 将解密后的结果转为 UTF-8 字符串 + const decryptedText = decrypted.toString(CryptoJS.enc.Utf8); + return decryptedText; +} + +// md5 签名 +const md5 = (key: string, data: string, timestamp: string) => { + const text = "s" + key + data + timestamp; + return CryptoJS.MD5(text).toString(CryptoJS.enc.Hex); +} + +// 生成签名戳 +const getFormattedDate = () => { + const date = new Date(); + const year = date.getFullYear(); // 获取年份 (yyyy) + const month = String(date.getMonth() + 1).padStart(2, '0'); // 获取月份 (MM) + const day = String(date.getDate()).padStart(2, '0'); // 获取日期 (dd) + const hours = String(date.getHours()).padStart(2, '0'); // 获取小时 (HH) + return `es0${year}${month}${day}${hours}0es`; // 拼接成 yyyyMMddHH 格式 +} + +// 加密方法 +const preprocessData = (data: string) => { + console.log('data', data); + // YYYYMMDD + let timestamp = getFormattedDate() + console.log('timestamp', timestamp); + // 秒为单位的时间戳 + let timeSecond = parseInt((new Date().getTime() / 1000).toString()) + console.log('timeSecond', timeSecond); + // 数据的加密 + let encryptionData = encryptAESECB(data, timestamp) + console.log('encryptionData', encryptionData); + // md5签名方法 + let md5Data = md5(timestamp, encryptionData, timestamp) + console.log('md5Data', md5Data); + + let res = { + data: encryptionData, + timestamp: timeSecond, + sign: md5Data + } + console.log('res', res); + + return res +} + +export default instance; diff --git a/umi4-admin-main.zip b/umi4-admin-main.zip index 76f655c..789f25d 100644 Binary files a/umi4-admin-main.zip and b/umi4-admin-main.zip differ diff --git a/yarn.lock b/yarn.lock index a078103..d0cc411 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4260,7 +4260,7 @@ camelcase-keys@^7.0.0: quick-lru "^5.1.1" type-fest "^1.2.1" -camelcase@^5.3.1: +camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== @@ -4367,6 +4367,15 @@ click-to-react-component@1.1.0: htm "^3.1.0" react-merge-refs "^1.1.0" +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + cliui@^8.0.1: version "8.0.1" resolved "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" @@ -4880,7 +4889,7 @@ decamelize-keys@^1.1.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.1.0: +decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== @@ -5013,6 +5022,11 @@ diffie-hellman@^5.0.3: miller-rabin "^4.0.0" randombytes "^2.0.0" +dijkstrajs@^1.0.1: + version "1.0.3" + resolved "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz#4c8dbdea1f0f6478bff94d9c49c784d623e4fc23" + integrity sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -6249,7 +6263,7 @@ gensync@^1.0.0-beta.2: resolved "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-caller-file@^2.0.5: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -8542,6 +8556,11 @@ pluralize@^8.0.0: resolved "https://registry.npmmirror.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== +pngjs@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb" + integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw== + point-in-polygon@^1.1.0: version "1.1.0" resolved "https://registry.npmmirror.com/point-in-polygon/-/point-in-polygon-1.1.0.tgz#b0af2616c01bdee341cbf2894df643387ca03357" @@ -9040,6 +9059,15 @@ qiankun@^2.10.1: lodash "^4.17.11" single-spa "^5.9.2" +qrcode@^1.5.4: + version "1.5.4" + resolved "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz#5cb81d86eb57c675febb08cf007fff963405da88" + integrity sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg== + dependencies: + dijkstrajs "^1.0.1" + pngjs "^5.0.0" + yargs "^15.3.1" + qs@6.13.0: version "6.13.0" resolved "https://registry.npmmirror.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" @@ -9870,6 +9898,11 @@ require-from-string@^2.0.2: resolved "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + resize-observer-polyfill@^1.5.1: version "1.5.1" resolved "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" @@ -10108,6 +10141,11 @@ serve-static@1.16.2: parseurl "~1.3.3" send "0.19.0" +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" @@ -11347,6 +11385,11 @@ which-collection@^1.0.1, which-collection@^1.0.2: is-weakmap "^2.0.2" is-weakset "^2.0.3" +which-module@^2.0.0: + version "2.0.1" + resolved "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== + which-typed-array@^1.1.16, which-typed-array@^1.1.18: version "1.1.18" resolved "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.18.tgz#df2389ebf3fbb246a71390e90730a9edb6ce17ad" @@ -11397,6 +11440,15 @@ word@~0.3.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -11458,6 +11510,11 @@ xtend@^4.0.0: resolved "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" @@ -11478,6 +11535,14 @@ yaml@^1.10.0: resolved "https://registry.npmmirror.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^20.2.9: version "20.2.9" resolved "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" @@ -11488,6 +11553,23 @@ yargs-parser@^21.1.1: resolved "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== +yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + yargs@^17.5.1: version "17.7.2" resolved "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"